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

View File

@ -21,6 +21,19 @@ class QgsGeometryEngine
#include "qgsgeometryengine.h" #include "qgsgeometryengine.h"
%End %End
public: public:
enum EngineOperationResult
{
Success,
NothingHappened,
MethodNotImplemented,
EngineError,
NodedGeometryError,
InvalidBaseGeometry,
InvalidInput,
SplitCannotSplitPoint,
};
virtual ~QgsGeometryEngine(); virtual ~QgsGeometryEngine();
virtual void geometryChanged() = 0; virtual void geometryChanged() = 0;
@ -230,12 +243,12 @@ class QgsGeometryEngine
:rtype: bool :rtype: bool
%End %End
virtual int splitGeometry( const QgsLineString &splitLine, virtual QgsGeometryEngine::EngineOperationResult splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries, QList<QgsAbstractGeometry *> &newGeometries,
bool topological, bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg = 0 ) const; QgsPointSequence &topologyTestPoints, QString *errorMsg = 0 ) const;
%Docstring %Docstring
:rtype: int :rtype: QgsGeometryEngine.EngineOperationResult
%End %End
virtual QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = 0 ) const = 0 /Factory/; 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 :rtype: QgsVectorLayer.EditResult
%End %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 %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 %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 %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 %End
int addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId ); QgsGeometry::OperationResult addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
%Docstring %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 %End
int addPart( const QgsPointSequence &ring, QgsFeatureId featureId ); QgsGeometry::OperationResult addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
%Docstring %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 %End
int addPart( QgsCurve *ring, QgsFeatureId featureId ) /PyName=addCurvedPart/; QgsGeometry::OperationResult addPart( QgsCurve *ring, QgsFeatureId featureId ) /PyName=addCurvedPart/;
%Docstring %Docstring
:rtype: int :rtype: QgsGeometry.OperationResult
%End %End
int translateFeature( QgsFeatureId featureId, double dx, double dy ); int translateFeature( QgsFeatureId featureId, double dx, double dy );
@ -98,14 +120,14 @@ class QgsVectorLayerEditUtils
:rtype: int :rtype: int
%End %End
int splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false ); QgsGeometry::OperationResult splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
%Docstring %Docstring
:rtype: int :rtype: QgsGeometry.OperationResult
%End %End
int splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false ); QgsGeometry::OperationResult splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
%Docstring %Docstring
:rtype: int :rtype: QgsGeometry.OperationResult
%End %End
int addTopologicalPoints( const QgsGeometry &geom ); int addTopologicalPoints( const QgsGeometry &geom );
@ -130,15 +152,6 @@ class QgsVectorLayerEditUtils
:rtype: int :rtype: int
%End %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; return sqrDist;
} }
int QgsGeometry::addRing( const QList<QgsPointXY> &ring ) QgsGeometry::OperationResult QgsGeometry::addRing( const QList<QgsPointXY> &ring )
{ {
detach( true ); detach( true );
@ -634,12 +634,12 @@ int QgsGeometry::addRing( const QList<QgsPointXY> &ring )
return addRing( ringLine ); return addRing( ringLine );
} }
int QgsGeometry::addRing( QgsCurve *ring ) QgsGeometry::OperationResult QgsGeometry::addRing( QgsCurve *ring )
{ {
if ( !d->geometry ) if ( !d->geometry )
{ {
delete ring; delete ring;
return 1; return InvalidInput;
} }
detach( true ); detach( true );
@ -647,14 +647,14 @@ int QgsGeometry::addRing( QgsCurve *ring )
return QgsGeometryEditUtils::addRing( d->geometry, 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; QgsPointSequence l;
convertPointList( points, l ); convertPointList( points, l );
return addPart( l, geomType ); 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; QgsAbstractGeometry *partGeom = nullptr;
if ( points.size() == 1 ) if ( points.size() == 1 )
@ -670,7 +670,7 @@ int QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryT
return addPart( partGeom, geomType ); return addPart( partGeom, geomType );
} }
int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType ) QgsGeometry::OperationResult QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType )
{ {
if ( !d->geometry ) if ( !d->geometry )
{ {
@ -687,7 +687,7 @@ int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType g
d->geometry = new QgsMultiPolygonV2(); d->geometry = new QgsMultiPolygonV2();
break; break;
default: default:
return 1; return QgsGeometry::AddPartNotMultiGeometry;
} }
} }
else else
@ -699,11 +699,15 @@ int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType g
return QgsGeometryEditUtils::addPart( d->geometry, part ); 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() ); 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 ); detach( true );
@ -757,24 +765,24 @@ int QgsGeometry::addPart( GEOSGeometry *newPart )
return QgsGeometryEditUtils::addPart( d->geometry, geom ); return QgsGeometryEditUtils::addPart( d->geometry, geom );
} }
int QgsGeometry::translate( double dx, double dy ) QgsGeometry::OperationResult QgsGeometry::translate( double dx, double dy )
{ {
if ( !d->geometry ) if ( !d->geometry )
{ {
return 1; return QgsGeometry::InvalidBaseGeometry;
} }
detach( true ); detach( true );
d->geometry->transform( QTransform::fromTranslate( dx, dy ) ); 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 ) if ( !d->geometry )
{ {
return 1; return QgsGeometry::InvalidBaseGeometry;
} }
detach( true ); detach( true );
@ -783,14 +791,14 @@ int QgsGeometry::rotate( double rotation, const QgsPointXY &center )
t.rotate( -rotation ); t.rotate( -rotation );
t.translate( -center.x(), -center.y() ); t.translate( -center.x(), -center.y() );
d->geometry->transform( t ); 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 ) if ( !d->geometry )
{ {
return 0; return InvalidBaseGeometry;
} }
QList<QgsAbstractGeometry *> newGeoms; QList<QgsAbstractGeometry *> newGeoms;
@ -798,9 +806,9 @@ int QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeo
QgsPointSequence tp; QgsPointSequence tp;
QgsGeos geos( d->geometry ); 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 ); detach( false );
d->geometry = newGeoms.at( 0 ); d->geometry = newGeoms.at( 0 );
@ -813,27 +821,69 @@ int QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeo
} }
convertPointList( tp, topologyTestPoints ); 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 ) if ( !d->geometry )
{ {
return 0; return InvalidBaseGeometry;
} }
QgsGeos geos( d->geometry ); QgsGeos geos( d->geometry );
int errorCode = 0; QgsGeometryEngine::EngineOperationResult errorCode = QgsGeometryEngine::Success;
QgsAbstractGeometry *geom = geos.reshapeGeometry( reshapeLineString, &errorCode ); QgsAbstractGeometry *geom = geos.reshapeGeometry( reshapeLineString, &errorCode );
if ( errorCode == 0 && geom ) if ( errorCode == QgsGeometryEngine::Success && geom )
{ {
detach( false ); detach( false );
delete d->geometry; delete d->geometry;
d->geometry = geom; 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 ) int QgsGeometry::makeDifferenceInPlace( const QgsGeometry &other )
@ -2091,28 +2141,28 @@ bool QgsGeometry::requiresConversionToStraightSegments() const
return d->geometry->hasCurvedSegments(); return d->geometry->hasCurvedSegments();
} }
int QgsGeometry::transform( const QgsCoordinateTransform &ct ) QgsGeometry::OperationResult QgsGeometry::transform( const QgsCoordinateTransform &ct )
{ {
if ( !d->geometry ) if ( !d->geometry )
{ {
return 1; return QgsGeometry::InvalidBaseGeometry;
} }
detach(); detach();
d->geometry->transform( ct ); 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 ) if ( !d->geometry )
{ {
return 1; return QgsGeometry::InvalidBaseGeometry;
} }
detach(); detach();
d->geometry->transform( ct ); d->geometry->transform( ct );
return 0; return QgsGeometry::Success;
} }
void QgsGeometry::mapToPixel( const QgsMapToPixel &mtp ) void QgsGeometry::mapToPixel( const QgsMapToPixel &mtp )

View File

@ -96,6 +96,30 @@ struct QgsGeometryPrivate;
class CORE_EXPORT QgsGeometry class CORE_EXPORT QgsGeometry
{ {
public: 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 //! Constructor
QgsGeometry(); QgsGeometry();
@ -390,62 +414,58 @@ class CORE_EXPORT QgsGeometry
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT ) const; double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT ) const;
#endif #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, * Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/ * \param ring The ring to be added
int addRing( const QList<QgsPointXY> &ring ); * \returns OperationResult a result code: success or reason of failure
// TODO QGIS 3.0 returns an enum instead of a magic constant */
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, * Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/ * \param ring The ring to be added
int addRing( QgsCurve *ring SIP_TRANSFER ); * \returns OperationResult a result code: success or reason of failure
// TODO QGIS 3.0 returns an enum instead of a magic constant */
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 points points describing part to add
* \param geomType default geometry type to create if no existing geometry * \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 * \returns OperationResult a result code: success or reason of failure
* not disjoint with existing polygons of the feature
*/ */
int addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPoints ); OperationResult 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
/** Adds a new part to a the geometry. /**
* Adds a new part to a the geometry.
* \param points points describing part to add * \param points points describing part to add
* \param geomType default geometry type to create if no existing geometry * \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 * \returns OperationResult a result code: success or reason of failure
* not disjoint with existing polygons of the feature
*/ */
int addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPointsV2 ); OperationResult addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPointsV2 );
// TODO QGIS 3.0 returns an enum instead of a magic constant
/** Adds a new part to this geometry. /**
* Adds a new part to this geometry.
* \param part part to add (ownership is transferred) * \param part part to add (ownership is transferred)
* \param geomType default geometry type to create if no existing geometry * \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 * \returns OperationResult a result code: success or reason of failure
* not disjoint with existing polygons of the feature
*/ */
int addPart( QgsAbstractGeometry *part SIP_TRANSFER, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ); OperationResult addPart( QgsAbstractGeometry *part SIP_TRANSFER, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry );
// TODO QGIS 3.0 returns an enum instead of a magic constant
/** 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. * \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 * \returns OperationResult a result code: success or reason of failure
* not disjoint with existing polygons of the feature * \note not available in python bindings
* \note not available in Python bindings
*/ */
int addPart( GEOSGeometry *newPart ) SIP_SKIP; OperationResult addPart( GEOSGeometry *newPart ) SIP_SKIP;
// TODO QGIS 3.0 returns an enum instead of a magic constant
/** 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 * Adds a new island polygon to a multipolygon feature
not disjoint with existing polygons of the feature * \returns OperationResult a result code: success or reason of failure
\note available in Python bindings as addPartGeometry * \note available in python bindings as addPartGeometry
\since QGIS 2.2
*/ */
int addPart( const QgsGeometry &newPart ) SIP_PYNAME( addPartGeometry ); OperationResult addPart( const QgsGeometry &newPart ) SIP_PYNAME( addPartGeometry );
// TODO QGIS 3.0 returns an enum instead of a magic constant
/** /**
* Removes the interior rings from a (multi)polygon geometry. If the minimumAllowedArea * 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; QgsGeometry removeInteriorRings( double minimumAllowedArea = -1 ) const;
/** Translate this geometry by dx, dy /**
\returns 0 in case of success*/ * Translate this geometry by dx, dy
int translate( double dx, double dy ); * \returns OperationResult a result code: success or reason of failure
/** 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
*/ */
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 * \param other geometry that should not be intersect
* \returns 0 in case of success
* \note Not available in Python * \note Not available in Python
*/ */
int makeDifferenceInPlace( const QgsGeometry &other ) SIP_SKIP; 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. * intersect the other geometry.
* \param other geometry that should not be intersect * \param other geometry that should not be intersect
* \returns difference geometry, or empty geometry if difference could not be calculated * \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 "qgsvectorlayer.h"
#include <limits> #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 ) if ( !ring )
{ {
return 1; return QgsGeometry::InvalidInput;
} }
QList< QgsCurvePolygon * > polygonList; QList< QgsCurvePolygon * > polygonList;
@ -50,23 +51,20 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
} }
else else
{ {
delete ring; return QgsGeometry::InvalidInput; //not polygon / multipolygon;
return 1; //not polygon / multipolygon;
} }
//ring must be closed //ring must be closed
if ( !ring->isClosed() ) if ( !ring->isClosed() )
{ {
delete ring; return QgsGeometry::AddRingNotClosed;
return 2;
} }
else if ( !ring->isRing() ) else if ( !ring->isRing() )
{ {
delete ring; return QgsGeometry::AddRingNotValid;
return 3;
} }
std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring ) ); std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) );
ringGeom->prepareGeometry(); ringGeom->prepareGeometry();
//for each polygon, test if inside outer ring and no intersection with other interior ring //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 ) ) ) if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) )
{ {
delete ring; return QgsGeometry::AddRingCrossesExistingRings;
return 4;
} }
} }
@ -92,59 +89,62 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
if ( QgsWkbTypes::hasM( geom->wkbType() ) ) if ( QgsWkbTypes::hasM( geom->wkbType() ) )
ring->addMValue( 0 ); ring->addMValue( 0 );
( *polyIter )->addInteriorRing( ring ); ( *polyIter )->addInteriorRing( ring.release() );
return 0; //success return QgsGeometry::Success; //success
} }
} }
delete ring; return QgsGeometry::AddRingNotInExistingFeature; //not contained in any outer ring
return 5; //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 ) if ( !geom )
{ {
return 1; return QgsGeometry::InvalidBaseGeometry;
} }
if ( !part ) if ( !part )
{ {
return 2; return QgsGeometry::InvalidInput;
} }
//multitype? //multitype?
QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom ); QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
if ( !geomCollection ) if ( !geomCollection )
{ {
return 1; return QgsGeometry::AddPartNotMultiGeometry;
} }
bool added = false; bool added = false;
if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface
|| QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon ) || 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 ) if ( curve && curve->isClosed() && curve->numPoints() >= 4 )
{ {
QgsCurvePolygon *poly = nullptr; std::unique_ptr<QgsCurvePolygon> poly;
if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString ) if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString )
{ {
poly = new QgsPolygonV2(); poly.reset( new QgsPolygonV2() );
} }
else else
{ {
poly = new QgsCurvePolygon(); poly.reset( new QgsCurvePolygon() );
} }
poly->setExteriorRing( curve ); poly->setExteriorRing( curve.release() );
added = geomCollection->addGeometry( poly ); added = geomCollection->addGeometry( poly.release() );
} }
else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon ) 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 ) 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 i;
int n = geomCollection->numGeometries(); int n = geomCollection->numGeometries();
@ -156,23 +156,19 @@ int QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometr
{ {
while ( geomCollection->numGeometries() > n ) while ( geomCollection->numGeometries() > n )
geomCollection->removeGeometry( n ); geomCollection->removeGeometry( n );
delete part; return QgsGeometry::InvalidInput;
return 2;
} }
delete part;
} }
else else
{ {
delete part; return QgsGeometry::InvalidInput;
return 2;
} }
} }
else 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 ) bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )

View File

@ -24,6 +24,7 @@ class QgsVectorLayer;
#define SIP_NO_FILE #define SIP_NO_FILE
#include "qgsfeature.h" #include "qgsfeature.h"
#include "qgsgeometry.h"
#include <QMap> #include <QMap>
/** \ingroup core /** \ingroup core
@ -36,19 +37,24 @@ class QgsGeometryEditUtils
{ {
public: public:
/** Adds interior ring (taking ownership). /**
\returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed, * Add an interior \a ring to a \a geometry.
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/ * Ownership of the \a ring is transferred.
// TODO QGIS 3.0 returns an enum instead of a magic constant * \returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
static int addRing( QgsAbstractGeometry *geom, QgsCurve *ring ); * 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 * Add a \a part to multi type \a geometry.
not disjoint with existing polygons of the feature*/ * Ownership of the \a part is transferred.
// TODO QGIS 3.0 returns an enum instead of a magic constant * \returns 0 in case of success, 1 if not a multigeometry, 2 if part is not a valid geometry, 3 if new polygon ring
static int addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *part ); * 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 * \returns true if delete was successful
*/ */
static bool deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum = 0 ); 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 "qgis_core.h"
#include "qgslinestring.h" #include "qgslinestring.h"
#include "qgsgeometry.h"
#include <QList> #include <QList>
@ -31,6 +32,24 @@ class QgsAbstractGeometry;
class CORE_EXPORT QgsGeometryEngine class CORE_EXPORT QgsGeometryEngine
{ {
public: 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 ~QgsGeometryEngine() = default;
virtual void geometryChanged() = 0; virtual void geometryChanged() = 0;
@ -190,17 +209,17 @@ class CORE_EXPORT QgsGeometryEngine
*/ */
virtual bool isSimple( QString *errorMsg = nullptr ) const = 0; virtual bool isSimple( QString *errorMsg = nullptr ) const = 0;
virtual int splitGeometry( const QgsLineString &splitLine, virtual QgsGeometryEngine::EngineOperationResult splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries, QList<QgsAbstractGeometry *> &newGeometries,
bool topological, bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg = nullptr ) const QgsPointSequence &topologyTestPoints, QString *errorMsg = nullptr ) const
{ {
Q_UNUSED( splitLine ); Q_UNUSED( splitLine );
Q_UNUSED( newGeometries ); Q_UNUSED( newGeometries );
Q_UNUSED( topological ); Q_UNUSED( topological );
Q_UNUSED( topologyTestPoints ); Q_UNUSED( topologyTestPoints );
Q_UNUSED( errorMsg ); 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; 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 "qgsmultipolygon.h"
#include "qgslogger.h" #include "qgslogger.h"
#include "qgspolygon.h" #include "qgspolygon.h"
#include "qgsgeometry.h"
#include <limits> #include <limits>
#include <cstdio> #include <cstdio>
#include <QtCore/qmath.h> #include <QtCore/qmath.h>
@ -519,32 +518,32 @@ double QgsGeos::length( QString *errorMsg ) const
return length; return length;
} }
int QgsGeos::splitGeometry( const QgsLineString &splitLine, QgsGeometryEngine::EngineOperationResult QgsGeos::splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries, QList<QgsAbstractGeometry *> &newGeometries,
bool topological, bool topological,
QgsPointSequence &topologyTestPoints, QgsPointSequence &topologyTestPoints,
QString *errorMsg ) const QString *errorMsg ) const
{ {
int returnCode = 0; EngineOperationResult returnCode = Success;
if ( !mGeometry || !mGeos ) if ( !mGeos || !mGeometry )
{ {
return 1; return InvalidBaseGeometry;
} }
//return if this type is point/multipoint //return if this type is point/multipoint
if ( mGeometry->dimension() == 0 ) if ( mGeometry->dimension() == 0 )
{ {
return 1; //cannot split points return SplitCannotSplitPoint; //cannot split points
} }
if ( !GEOSisValid_r( geosinit.ctxt, mGeos ) ) if ( !GEOSisValid_r( geosinit.ctxt, mGeos ) )
return 7; return InvalidBaseGeometry;
//make sure splitLine is valid //make sure splitLine is valid
if ( ( mGeometry->dimension() == 1 && splitLine.numPoints() < 1 ) || if ( ( mGeometry->dimension() == 1 && splitLine.numPoints() < 1 ) ||
( mGeometry->dimension() == 2 && splitLine.numPoints() < 2 ) ) ( mGeometry->dimension() == 2 && splitLine.numPoints() < 2 ) )
return 1; return InvalidInput;
newGeometries.clear(); newGeometries.clear();
GEOSGeometry *splitLineGeos = nullptr; GEOSGeometry *splitLineGeos = nullptr;
@ -561,20 +560,22 @@ int QgsGeos::splitGeometry( const QgsLineString &splitLine,
} }
else else
{ {
return 1; return InvalidInput;
} }
if ( !GEOSisValid_r( geosinit.ctxt, splitLineGeos ) || !GEOSisSimple_r( geosinit.ctxt, splitLineGeos ) ) if ( !GEOSisValid_r( geosinit.ctxt, splitLineGeos ) || !GEOSisSimple_r( geosinit.ctxt, splitLineGeos ) )
{ {
GEOSGeom_destroy_r( geosinit.ctxt, splitLineGeos ); GEOSGeom_destroy_r( geosinit.ctxt, splitLineGeos );
return 1; return InvalidInput;
} }
if ( topological ) if ( topological )
{ {
//find out candidate points for topological corrections //find out candidate points for topological corrections
if ( topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) != 0 ) if ( !topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) )
return 1; {
return InvalidInput; // TODO: is it really an invalid input?
}
} }
//call split function depending on geometry type //call split function depending on geometry type
@ -590,17 +591,17 @@ int QgsGeos::splitGeometry( const QgsLineString &splitLine,
} }
else else
{ {
return 1; return InvalidInput;
} }
} }
CATCH_GEOS_WITH_ERRMSG( 2 ) CATCH_GEOS_WITH_ERRMSG( EngineError )
return returnCode; 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. //Find out the intersection points between splitLineGeos and this geometry.
//These points need to be tested for topological correctness by the calling function //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 ) if ( !mGeos )
{ {
return 1; return false;
} }
try try
@ -616,7 +617,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint
testPoints.clear(); testPoints.clear();
GEOSGeometry *intersectionGeom = GEOSIntersection_r( geosinit.ctxt, mGeos, splitLine ); GEOSGeometry *intersectionGeom = GEOSIntersection_r( geosinit.ctxt, mGeos, splitLine );
if ( !intersectionGeom ) if ( !intersectionGeom )
return 1; return false;
bool simple = false; bool simple = false;
int nIntersectGeoms = 1; int nIntersectGeoms = 1;
@ -656,7 +657,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint
} }
CATCH_GEOS_WITH_ERRMSG( 1 ) CATCH_GEOS_WITH_ERRMSG( 1 )
return 0; return true;
} }
GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const
@ -725,22 +726,22 @@ GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const
return asGeos( &lines, mPrecision ); 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 ) if ( !splitLine )
return 2; return InvalidInput;
if ( !mGeos ) if ( !mGeos )
return 5; return InvalidBaseGeometry;
//first test if linestring intersects geometry. If not, return straight away //first test if linestring intersects geometry. If not, return straight away
if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) ) if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) )
return 1; return NothingHappened;
//check that split line has no linear intersection //check that split line has no linear intersection
int linearIntersect = GEOSRelatePattern_r( geosinit.ctxt, mGeos, splitLine, "1********" ); int linearIntersect = GEOSRelatePattern_r( geosinit.ctxt, mGeos, splitLine, "1********" );
if ( linearIntersect > 0 ) if ( linearIntersect > 0 )
return 3; return InvalidInput;
int splitGeomType = GEOSGeomTypeId_r( geosinit.ctxt, splitLine ); int splitGeomType = GEOSGeomTypeId_r( geosinit.ctxt, splitLine );
@ -778,25 +779,25 @@ int QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeom
} }
GEOSGeom_destroy_r( geosinit.ctxt, splitGeom ); 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 ) if ( !splitLine )
return 2; return InvalidInput;
if ( !mGeos ) if ( !mGeos )
return 5; return InvalidBaseGeometry;
//first test if linestring intersects geometry. If not, return straight away //first test if linestring intersects geometry. If not, return straight away
if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) ) 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) //first union all the polygon rings together (to get them noded, see JTS developer guide)
GEOSGeometry *nodedGeometry = nodeGeometries( splitLine, mGeos ); GEOSGeometry *nodedGeometry = nodeGeometries( splitLine, mGeos );
if ( !nodedGeometry ) if ( !nodedGeometry )
return 2; //an error occurred during noding return NodedGeometryError; //an error occurred during noding
GEOSGeometry *polygons = GEOSPolygonize_r( geosinit.ctxt, &nodedGeometry, 1 ); GEOSGeometry *polygons = GEOSPolygonize_r( geosinit.ctxt, &nodedGeometry, 1 );
if ( !polygons || numberOfGeometries( polygons ) == 0 ) if ( !polygons || numberOfGeometries( polygons ) == 0 )
@ -806,7 +807,7 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry ); GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
return 4; return InvalidBaseGeometry;
} }
GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry ); GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
@ -859,7 +860,7 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
{ {
GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] ); GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] );
} }
return 1; return NothingHappened;
} }
int i; int i;
@ -871,13 +872,13 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
for ( i = 0; i < testedGeometries.size(); ++i ) for ( i = 0; i < testedGeometries.size(); ++i )
GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] ); GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] );
return 3; return InvalidBaseGeometry;
} }
for ( i = 0; i < testedGeometries.size(); ++i ) for ( i = 0; i < testedGeometries.size(); ++i )
newGeometries << fromGeos( testedGeometries[i] ); newGeometries << fromGeos( testedGeometries[i] );
return 0; return Success;
} }
GEOSGeometry *QgsGeos::nodeGeometries( const GEOSGeometry *splitLine, const GEOSGeometry *geom ) GEOSGeometry *QgsGeos::nodeGeometries( const GEOSGeometry *splitLine, const GEOSGeometry *geom )
@ -1892,11 +1893,17 @@ QgsAbstractGeometry *QgsGeos::singleSidedBuffer( double distance, int segments,
return fromGeos( geos.get() ); 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; return nullptr;
} }
@ -1906,7 +1913,7 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
int numGeoms = GEOSGetNumGeometries_r( geosinit.ctxt, mGeos ); int numGeoms = GEOSGetNumGeometries_r( geosinit.ctxt, mGeos );
if ( numGeoms == -1 ) if ( numGeoms == -1 )
{ {
if ( errorCode ) { *errorCode = 1; } if ( errorCode ) { *errorCode = InvalidBaseGeometry; }
GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos ); GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
return nullptr; return nullptr;
} }
@ -1930,7 +1937,8 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
reshapedGeometry = reshapePolygon( mGeos, reshapeLineGeos, mPrecision ); reshapedGeometry = reshapePolygon( mGeos, reshapeLineGeos, mPrecision );
} }
if ( errorCode ) { *errorCode = 0; } if ( errorCode )
*errorCode = Success;
QgsAbstractGeometry *reshapeResult = fromGeos( reshapedGeometry ); QgsAbstractGeometry *reshapeResult = fromGeos( reshapedGeometry );
GEOSGeom_destroy_r( geosinit.ctxt, reshapedGeometry ); GEOSGeom_destroy_r( geosinit.ctxt, reshapedGeometry );
GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos ); GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
@ -1978,13 +1986,14 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
delete[] newGeoms; delete[] newGeoms;
if ( !newMultiGeom ) if ( !newMultiGeom )
{ {
if ( errorCode ) { *errorCode = 3; } if ( errorCode ) { *errorCode = EngineError; }
return nullptr; return nullptr;
} }
if ( reshapeTookPlace ) if ( reshapeTookPlace )
{ {
if ( errorCode ) { *errorCode = 0; } if ( errorCode )
*errorCode = Success;
QgsAbstractGeometry *reshapedMultiGeom = fromGeos( newMultiGeom ); QgsAbstractGeometry *reshapedMultiGeom = fromGeos( newMultiGeom );
GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom ); GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom );
return reshapedMultiGeom; return reshapedMultiGeom;
@ -1992,7 +2001,7 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
else else
{ {
GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom ); GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom );
if ( errorCode ) { *errorCode = 1; } if ( errorCode ) { *errorCode = NothingHappened; }
return nullptr; return nullptr;
} }
} }

View File

@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgis_core.h" #include "qgis_core.h"
#include "qgsgeometryengine.h" #include "qgsgeometryengine.h"
#include "qgsgeometry.h"
#include <geos_c.h> #include <geos_c.h>
class QgsLineString; 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] topologyTestPoints points that need to be tested for topological completeness in the dataset
\param[out] errorMsg error messages emitted, if any \param[out] errorMsg error messages emitted, if any
\returns 0 in case of success, 1 if geometry has not been split, error else*/ \returns 0 in case of success, 1 if geometry has not been split, error else*/
int splitGeometry( const QgsLineString &splitLine, EngineOperationResult splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries, QList<QgsAbstractGeometry *> &newGeometries,
bool topological, bool topological,
QgsPointSequence &topologyTestPoints, QgsPointSequence &topologyTestPoints,
QString *errorMsg = nullptr ) const override; QString *errorMsg = nullptr ) const override;
QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, 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, int joinStyle, double miterLimit,
QString *errorMsg = nullptr ) const; 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 /** Merges any connected lines in a LineString/MultiLineString geometry and
* converts them to single line strings. * converts them to single line strings.
@ -261,10 +268,10 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
static GEOSGeometry *createGeosPolygon( const QgsAbstractGeometry *poly, double precision ); static GEOSGeometry *createGeosPolygon( const QgsAbstractGeometry *poly, double precision );
//utils for geometry split //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; GEOSGeometry *linePointDifference( GEOSGeometry *GEOSsplitPoint ) const;
int splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const; EngineOperationResult splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
int splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const; EngineOperationResult splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
//utils for reshape //utils for reshape
static GEOSGeometry *reshapeLine( const GEOSGeometry *line, const GEOSGeometry *reshapeLineGeos, double precision ); static GEOSGeometry *reshapeLine( const GEOSGeometry *line, const GEOSGeometry *reshapeLineGeos, double precision );

View File

@ -29,41 +29,41 @@
QgsVectorLayerEditUtils::QgsVectorLayerEditUtils( QgsVectorLayer *layer ) QgsVectorLayerEditUtils::QgsVectorLayerEditUtils( QgsVectorLayer *layer )
: L( layer ) : mLayer( layer )
{ {
} }
bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex ) bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return false; return false;
QgsFeature f; 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 return false; // geometry not found
QgsGeometry geometry = f.geometry(); QgsGeometry geometry = f.geometry();
geometry.insertVertex( x, y, beforeVertex ); geometry.insertVertex( x, y, beforeVertex );
L->editBuffer()->changeGeometry( atFeatureId, geometry ); mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
return true; return true;
} }
bool QgsVectorLayerEditUtils::insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex ) bool QgsVectorLayerEditUtils::insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return false; return false;
QgsFeature f; 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 return false; // geometry not found
QgsGeometry geometry = f.geometry(); QgsGeometry geometry = f.geometry();
geometry.insertVertex( point, beforeVertex ); geometry.insertVertex( point, beforeVertex );
L->editBuffer()->changeGeometry( atFeatureId, geometry ); mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
return true; 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 ) bool QgsVectorLayerEditUtils::moveVertex( const QgsPoint &p, QgsFeatureId atFeatureId, int atVertex )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return false; return false;
QgsFeature f; 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 return false; // geometry not found
QgsGeometry geometry = f.geometry(); QgsGeometry geometry = f.geometry();
geometry.moveVertex( p, atVertex ); geometry.moveVertex( p, atVertex );
L->editBuffer()->changeGeometry( atFeatureId, geometry ); mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
return true; return true;
} }
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId featureId, int vertex ) QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId featureId, int vertex )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return QgsVectorLayer::InvalidLayer; return QgsVectorLayer::InvalidLayer;
QgsFeature f; 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 return QgsVectorLayer::FetchFeatureFailed; // geometry not found
QgsGeometry geometry = f.geometry(); QgsGeometry geometry = f.geometry();
@ -111,38 +111,38 @@ QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId f
geometry.setGeometry( nullptr ); geometry.setGeometry( nullptr );
} }
L->editBuffer()->changeGeometry( featureId, geometry ); mLayer->editBuffer()->changeGeometry( featureId, geometry );
return !geometry.isNull() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry; 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 ); QgsLineString *ringLine = new QgsLineString( ring );
return addRing( ringLine, targetFeatureIds, modifiedFeatureId ); 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; 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; QgsFeature f;
QgsFeatureIterator fit; QgsFeatureIterator fit;
if ( !targetFeatureIds.isEmpty() ) if ( !targetFeatureIds.isEmpty() )
{ {
//check only specified features //check only specified features
fit = L->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) ); fit = mLayer->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) );
} }
else else
{ {
//check all intersecting features //check all intersecting features
QgsRectangle bBox = ring->boundingBox(); 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 //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() ) ); addRingReturnCode = g.addRing( static_cast< QgsCurve * >( ring->clone() ) );
if ( addRingReturnCode == 0 ) if ( addRingReturnCode == 0 )
{ if ( addRingReturnCode == QgsGeometry::Success )
L->editBuffer()->changeGeometry( f.id(), g ); {
if ( modifiedFeatureId ) mLayer->editBuffer()->changeGeometry( f.id(), g );
*modifiedFeatureId = f.id(); if ( modifiedFeatureId )
*modifiedFeatureId = f.id();
//setModified( true, true ); //setModified( true, true );
break; break;
} }
} }
delete ring; delete ring;
return addRingReturnCode; return addRingReturnCode;
} }
int QgsVectorLayerEditUtils::addPart( const QList<QgsPointXY> &points, QgsFeatureId featureId ) QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QList<QgsPointXY> &points, QgsFeatureId featureId )
{ {
QgsPointSequence l; QgsPointSequence l;
for ( QList<QgsPointXY>::const_iterator it = points.constBegin(); it != points.constEnd(); ++it ) 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 ); return addPart( l, featureId );
} }
int QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeatureId featureId ) QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeatureId featureId )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return 6; return QgsGeometry::AddPartSelectedGeometryNotFound;
QgsGeometry geometry; QgsGeometry geometry;
bool firstPart = false; bool firstPart = false;
QgsFeature f; QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) ) if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
return 6; //not found return QgsGeometry::AddPartSelectedGeometryNotFound; //not found
if ( !f.hasGeometry() ) if ( !f.hasGeometry() )
{ {
@ -201,30 +202,30 @@ int QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeature
geometry = f.geometry(); geometry = f.geometry();
} }
int errorCode = geometry.addPart( points, L->geometryType() ) ; QgsGeometry::OperationResult errorCode = geometry.addPart( points, mLayer->geometryType() ) ;
if ( errorCode == 0 ) if ( errorCode == QgsGeometry::Success )
{ {
if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() ) if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() )
&& L->dataProvider()->doesStrictFeatureTypeCheck() ) && mLayer->dataProvider()->doesStrictFeatureTypeCheck() )
{ {
//convert back to single part if required by layer //convert back to single part if required by layer
geometry.convertToSingleType(); geometry.convertToSingleType();
} }
L->editBuffer()->changeGeometry( featureId, geometry ); mLayer->editBuffer()->changeGeometry( featureId, geometry );
} }
return errorCode; return errorCode;
} }
int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId ) QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return 6; return QgsGeometry::AddPartSelectedGeometryNotFound;
QgsGeometry geometry; QgsGeometry geometry;
bool firstPart = false; bool firstPart = false;
QgsFeature f; QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) ) if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
return 6; //not found return QgsGeometry::AddPartSelectedGeometryNotFound;
if ( !f.hasGeometry() ) if ( !f.hasGeometry() )
{ {
@ -236,16 +237,16 @@ int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId )
geometry = f.geometry(); geometry = f.geometry();
} }
int errorCode = geometry.addPart( ring, L->geometryType() ) ; QgsGeometry::OperationResult errorCode = geometry.addPart( ring, mLayer->geometryType() ) ;
if ( errorCode == 0 ) if ( errorCode == QgsGeometry::Success )
{ {
if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() ) if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() )
&& L->dataProvider()->doesStrictFeatureTypeCheck() ) && mLayer->dataProvider()->doesStrictFeatureTypeCheck() )
{ {
//convert back to single part if required by layer //convert back to single part if required by layer
geometry.convertToSingleType(); geometry.convertToSingleType();
} }
L->editBuffer()->changeGeometry( featureId, geometry ); mLayer->editBuffer()->changeGeometry( featureId, geometry );
} }
return errorCode; return errorCode;
} }
@ -253,11 +254,11 @@ int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId )
int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy ) int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return 1; return 1;
QgsFeature f; 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 return 1; //geometry not found
QgsGeometry geometry = f.geometry(); QgsGeometry geometry = f.geometry();
@ -265,30 +266,30 @@ int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx
int errorCode = geometry.translate( dx, dy ); int errorCode = geometry.translate( dx, dy );
if ( errorCode == 0 ) if ( errorCode == 0 )
{ {
L->editBuffer()->changeGeometry( featureId, geometry ); mLayer->editBuffer()->changeGeometry( featureId, geometry );
} }
return errorCode; return errorCode;
} }
int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing ) QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return 4; return QgsGeometry::InvalidBaseGeometry;
QgsFeatureList newFeatures; //store all the newly created features QgsFeatureList newFeatures; //store all the newly created features
double xMin, yMin, xMax, yMax; double xMin, yMin, xMax, yMax;
QgsRectangle bBox; //bounding box of the split line QgsRectangle bBox; //bounding box of the split line
int returnCode = 0; QgsGeometry::OperationResult returnCode = QgsGeometry::Success;
int splitFunctionReturn; //return code of QgsGeometry::splitGeometry QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
int numberOfSplitFeatures = 0; int numberOfSplitFeatures = 0;
QgsFeatureIterator features; 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 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 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 else
{ {
return 1; return QgsGeometry::InvalidInput;
} }
if ( bBox.isEmpty() ) 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 //If we have a single point, we still create a non-null box
double bufferDistance = 0.000001; double bufferDistance = 0.000001;
if ( L->crs().isGeographic() ) if ( mLayer->crs().isGeographic() )
bufferDistance = 0.00000001; bufferDistance = 0.00000001;
bBox.setXMinimum( bBox.xMinimum() - bufferDistance ); bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
bBox.setXMaximum( bBox.xMaximum() + 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; QgsFeature feat;
@ -344,16 +345,16 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
QList<QgsPointXY> topologyTestPoints; QList<QgsPointXY> topologyTestPoints;
QgsGeometry featureGeom = feat.geometry(); QgsGeometry featureGeom = feat.geometry();
splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints ); splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
if ( splitFunctionReturn == 0 ) if ( splitFunctionReturn == QgsGeometry::Success )
{ {
//change this geometry //change this geometry
L->editBuffer()->changeGeometry( feat.id(), featureGeom ); mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
//insert new features //insert new features
for ( int i = 0; i < newGeometries.size(); ++i ) for ( int i = 0; i < newGeometries.size(); ++i )
{ {
QgsFeature f = QgsVectorLayerUtils::createFeature( L, newGeometries.at( i ), feat.attributes().toMap() ); QgsFeature f = QgsVectorLayerUtils::createFeature( mLayer, newGeometries.at( i ), feat.attributes().toMap() );
L->editBuffer()->addFeature( f ); mLayer->editBuffer()->addFeature( f );
} }
if ( topologicalEditing ) if ( topologicalEditing )
@ -366,7 +367,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
} }
++numberOfSplitFeatures; ++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; returnCode = splitFunctionReturn;
} }
@ -376,28 +377,28 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
{ {
//There is a selection but no feature has been split. //There is a selection but no feature has been split.
//Maybe user forgot that only the selected features are split //Maybe user forgot that only the selected features are split
returnCode = 4; returnCode = QgsGeometry::NothingHappened;
} }
return returnCode; return returnCode;
} }
int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing ) QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return 4; return QgsGeometry::InvalidBaseGeometry;
double xMin, yMin, xMax, yMax; double xMin, yMin, xMax, yMax;
QgsRectangle bBox; //bounding box of the split line QgsRectangle bBox; //bounding box of the split line
int returnCode = 0; QgsGeometry::OperationResult returnCode = QgsGeometry::Success;
int splitFunctionReturn; //return code of QgsGeometry::splitGeometry QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
int numberOfSplitParts = 0; int numberOfSplitParts = 0;
QgsFeatureIterator fit; 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 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 else
{ {
return 1; return QgsGeometry::InvalidInput;
} }
if ( bBox.isEmpty() ) 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 //If we have a single point, we still create a non-null box
double bufferDistance = 0.000001; double bufferDistance = 0.000001;
if ( L->crs().isGeographic() ) if ( mLayer->crs().isGeographic() )
bufferDistance = 0.00000001; bufferDistance = 0.00000001;
bBox.setXMinimum( bBox.xMinimum() - bufferDistance ); bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
bBox.setXMaximum( bBox.xMaximum() + 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; int addPartRet = 0;
@ -469,7 +470,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
if ( !addPartRet ) if ( !addPartRet )
{ {
L->editBuffer()->changeGeometry( feat.id(), featureGeom ); mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
} }
else else
{ {
@ -489,7 +490,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
break; break;
} }
} }
L->editBuffer()->changeGeometry( feat.id(), featureGeom ); mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
if ( topologicalEditing ) 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. //There is a selection but no feature has been split.
//Maybe user forgot that only the selected features are split //Maybe user forgot that only the selected features are split
returnCode = 4; returnCode = QgsGeometry::NothingHappened;
} }
return returnCode; return returnCode;
@ -520,7 +521,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom ) int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return 1; return 1;
if ( geom.isNull() ) if ( geom.isNull() )
@ -628,18 +629,18 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom )
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
{ {
if ( !L->isSpatial() ) if ( !mLayer->isSpatial() )
return 1; 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 //work with a tolerance because coordinate projection may introduce some rounding
double threshold = 0.0000001; double threshold = 0.0000001;
if ( L->crs().mapUnits() == QgsUnitTypes::DistanceMeters ) if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceMeters )
{ {
threshold = 0.001; threshold = 0.001;
} }
else if ( L->crs().mapUnits() == QgsUnitTypes::DistanceFeet ) else if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceFeet )
{ {
threshold = 0.0001; threshold = 0.0001;
} }
@ -649,7 +650,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
double sqrSnappingTolerance = threshold * threshold; double sqrSnappingTolerance = threshold * threshold;
QgsFeature f; QgsFeature f;
QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest() QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest()
.setFilterRect( searchRect ) .setFilterRect( searchRect )
.setFlags( QgsFeatureRequest::ExactIntersect ) .setFlags( QgsFeatureRequest::ExactIntersect )
.setSubsetOfAttributes( QgsAttributeList() ) ); .setSubsetOfAttributes( QgsAttributeList() ) );
@ -685,7 +686,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
if ( sqrDistVertexSnap < sqrSnappingTolerance ) if ( sqrDistVertexSnap < sqrSnappingTolerance )
continue; // the vertex already exists - do not insert it 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" ); 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 ) if ( list.size() < 1 )
{ {
return 1; return false;
} }
xmin = std::numeric_limits<double>::max(); 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_core.h"
#include "qgis.h" #include "qgis.h"
#include "qgsfeature.h" #include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include "qgsgeometry.h" #include "qgsgeometry.h"
#include "qgsvectorlayer.h"
class QgsCurve; class QgsCurve;
@ -66,68 +65,40 @@ class CORE_EXPORT QgsVectorLayerEditUtils
QgsVectorLayer::EditResult deleteVertex( QgsFeatureId featureId, int vertex ); QgsVectorLayer::EditResult deleteVertex( QgsFeatureId featureId, int vertex );
/** Adds a ring to polygon/multipolygon features /** Adds a ring to polygon/multipolygon features
* \param ring ring to add * @param ring ring to add
* \param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise * @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. * 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 * @param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
* \returns * @return OperationResult result code: success or reason of failure
* 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
*/ */
// TODO QGIS 3.0 returns an enum instead of a magic constant QgsGeometry::OperationResult addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr );
int addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr );
/** Adds a ring to polygon/multipolygon features /**
* \param ring ring to add * Adds a ring to polygon/multipolygon features
* \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. * 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 * @param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
* \returns * @return OperationResult result code: success or reason of failure
* 0 in case of success, * @note available in python bindings as addCurvedRing
* 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
*/ */
// TODO QGIS 3.0 returns an enum instead of a magic constant QgsGeometry::OperationResult addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ) SIP_PYNAME( addCurvedRing );
int addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ) SIP_PYNAME( addCurvedRing );
/** Adds a new part polygon to a multipart feature /**
* \returns * Adds a new part polygon to a multipart feature
* 0 in case of success, * @returns QgsGeometry::OperationResult a result code: success or reason of failure
* 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
*/ */
// TODO QGIS 3.0 returns an enum instead of a magic constant QgsGeometry::OperationResult addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
int addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
/** Adds a new part polygon to a multipart feature /**
* \returns * Adds a new part polygon to a multipart feature
* 0 in case of success, * @returns QgsGeometry::OperationResult a result code: success or reason of failure
* 1 if selected feature is not multipart, * @note available in python bindings as addPartV2
* 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
*/ */
// TODO QGIS 3.0 returns an enum instead of a magic constant QgsGeometry::OperationResult addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
int addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
//! \note available in Python bindings as addCurvedPart // @note available in python bindings as addCurvedPart
// TODO QGIS 3.0 returns an enum instead of a magic constant QgsGeometry::OperationResult addPart( QgsCurve *ring, QgsFeatureId featureId ) SIP_PYNAME( addCurvedPart );
int addPart( QgsCurve *ring, QgsFeatureId featureId ) SIP_PYNAME( addCurvedPart );
/** Translates feature by dx, dy /** Translates feature by dx, dy
* \param featureId id of the feature to translate * \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 * 4 if there is a selection but no feature split
*/ */
// TODO QGIS 3.0 returns an enum instead of a magic constant // 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 /** Splits features cut by the given line
* \param splitLine line that splits the layer features * \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 * 4 if there is a selection but no feature split
*/ */
// TODO QGIS 3.0 returns an enum instead of a magic constant // 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. /** Adds topological points for every vertex of the geometry.
* \param geom the geometry where each vertex is added to segments of other features * \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 ); 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: 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 #endif // QGSVECTORLAYEREDITUTILS_H

View File

@ -1423,14 +1423,14 @@ class TestQgsGeometry(unittest.TestCase):
] ]
polyline = QgsGeometry.fromPolyline(points[0]) 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:1]), QgsGeometry.InvalidInput, "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: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))" expwkt = "MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 0, 3 1))"
wkt = polyline.exportToWkt() wkt = polyline.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
polyline = QgsGeometry.fromPolyline(points[0]) 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))" 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() wkt = polyline.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) 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 = QgsGeometry.fromPolyline(points[0])
polyline.geometry().addZValue(4.0) polyline.geometry().addZValue(4.0)
points2 = [QgsPoint(p[0], p[1], 3.0, wkbType=QgsWkbTypes.PointZ) for p in points[1]] 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))" 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() wkt = polyline.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) 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]) 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:1]), QgsGeometry.InvalidInput, "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:2]), QgsGeometry.InvalidInput, "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(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)]), 2, "addPoints with 'closed' 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)))" 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() wkt = polygon.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) 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]) mp = QgsGeometry.fromMultiPolygon(points[:1])
p = QgsGeometry.fromPolygon(points[1]) p = QgsGeometry.fromPolygon(points[1])
self.assertEqual(mp.addPartGeometry(p), 0) self.assertEqual(mp.addPartGeometry(p), QgsGeometry.Success)
wkt = mp.exportToWkt() wkt = mp.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
mp = QgsGeometry.fromMultiPolygon(points[:1]) mp = QgsGeometry.fromMultiPolygon(points[:1])
mp2 = 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() wkt = mp.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) 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 = QgsGeometry.fromPolygon(points[0])
polygon.geometry().addZValue(4.0) polygon.geometry().addZValue(4.0)
points2 = [QgsPoint(pi[0], pi[1], 3.0, wkbType=QgsWkbTypes.PointZ) for pi in points[1][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)))" 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() wkt = polygon.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@ -1492,38 +1492,38 @@ class TestQgsGeometry(unittest.TestCase):
empty = QgsGeometry() empty = QgsGeometry()
# if not default type specified, addPart should fail # if not default type specified, addPart should fail
result = empty.addPoints([QgsPointXY(4, 0)]) 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) 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() wkt = empty.exportToWkt()
expwkt = 'MultiPoint ((4 0))' expwkt = 'MultiPoint ((4 0))'
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
result = empty.addPoints([QgsPointXY(5, 1)]) 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() wkt = empty.exportToWkt()
expwkt = 'MultiPoint ((4 0),(5 1))' expwkt = 'MultiPoint ((4 0),(5 1))'
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
# next try with lines # next try with lines
empty = QgsGeometry() empty = QgsGeometry()
result = empty.addPoints(points[0][0], QgsWkbTypes.LineGeometry) 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() wkt = empty.exportToWkt()
expwkt = 'MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0))' 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) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
result = empty.addPoints(points[1][0]) 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() 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))' 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) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
# finally try with polygons # finally try with polygons
empty = QgsGeometry() empty = QgsGeometry()
result = empty.addPoints(points[0][0], QgsWkbTypes.PolygonGeometry) 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() wkt = empty.exportToWkt()
expwkt = 'MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)))' 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) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
result = empty.addPoints(points[1][0]) 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() 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)))' 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) assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)