Skip to content

Commit 80e8ff0

Browse files
authoredAug 14, 2017
Merge pull request #5014 from m-kuhn/enumgeomfunc
Return enum instead of int from QgsGeometry operations
2 parents 904cd6f + 3d53a2d commit 80e8ff0

13 files changed

+959
-715
lines changed
 

‎python/core/geometry/qgsgeometry.sip‎

Lines changed: 120 additions & 108 deletions
Large diffs are not rendered by default.

‎python/core/geometry/qgsgeometryengine.sip‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ class QgsGeometryEngine
2121
#include "qgsgeometryengine.h"
2222
%End
2323
public:
24+
25+
enum EngineOperationResult
26+
{
27+
Success,
28+
NothingHappened,
29+
MethodNotImplemented,
30+
EngineError,
31+
NodedGeometryError,
32+
InvalidBaseGeometry,
33+
InvalidInput,
34+
SplitCannotSplitPoint,
35+
};
36+
2437
virtual ~QgsGeometryEngine();
2538

2639
virtual void geometryChanged() = 0;
@@ -230,12 +243,12 @@ class QgsGeometryEngine
230243
:rtype: bool
231244
%End
232245

233-
virtual int splitGeometry( const QgsLineString &splitLine,
234-
QList<QgsAbstractGeometry *> &newGeometries,
235-
bool topological,
236-
QgsPointSequence &topologyTestPoints, QString *errorMsg = 0 ) const;
246+
virtual QgsGeometryEngine::EngineOperationResult splitGeometry( const QgsLineString &splitLine,
247+
QList<QgsAbstractGeometry *> &newGeometries,
248+
bool topological,
249+
QgsPointSequence &topologyTestPoints, QString *errorMsg = 0 ) const;
237250
%Docstring
238-
:rtype: int
251+
:rtype: QgsGeometryEngine.EngineOperationResult
239252
%End
240253

241254
virtual QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = 0 ) const = 0 /Factory/;

‎python/core/qgsvectorlayereditutils.sip‎

Lines changed: 85 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010

1111

12-
1312
class QgsVectorLayerEditUtils
1413
{
1514

@@ -22,32 +21,32 @@ class QgsVectorLayerEditUtils
2221
bool insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex );
2322
%Docstring
2423
Insert a new vertex before the given vertex number,
25-
in the given ring, item (first number is index 0), and feature
26-
Not meaningful for Point geometries
24+
in the given ring, item (first number is index 0), and feature
25+
Not meaningful for Point geometries
2726
:rtype: bool
2827
%End
2928

3029
bool insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex );
3130
%Docstring
32-
Insert a new vertex before the given vertex number,
33-
in the given ring, item (first number is index 0), and feature
34-
Not meaningful for Point geometries
31+
Inserts a new vertex before the given vertex number,
32+
in the given ring, item (first number is index 0), and feature
33+
Not meaningful for Point geometries
3534
:rtype: bool
3635
%End
3736

3837
bool moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex );
3938
%Docstring
4039
Moves the vertex at the given position number,
41-
ring and item (first number is index 0), and feature
42-
to the given coordinates
40+
ring and item (first number is index 0), and feature
41+
to the given coordinates
4342
:rtype: bool
4443
%End
4544

4645
bool moveVertex( const QgsPoint &p, QgsFeatureId atFeatureId, int atVertex ) /PyName=moveVertexV2/;
4746
%Docstring
4847
Moves the vertex at the given position number,
49-
ring and item (first number is index 0), and feature
50-
to the given coordinates
48+
ring and item (first number is index 0), and feature
49+
to the given coordinates
5150
.. note::
5251

5352
available in Python bindings as moveVertexV2
@@ -63,29 +62,74 @@ class QgsVectorLayerEditUtils
6362
:rtype: QgsVectorLayer.EditResult
6463
%End
6564

66-
int addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 );
65+
QgsGeometry::OperationResult addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 );
6766
%Docstring
68-
:rtype: int
67+
Adds a ring to polygon/multipolygon features
68+
\param ring ring to add
69+
\param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
70+
all intersecting features are tested and the ring is added to the first valid feature.
71+
\param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
72+
:return: OperationResult result code: success or reason of failure
73+
:rtype: QgsGeometry.OperationResult
6974
%End
7075

71-
int addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 ) /PyName=addCurvedRing/;
76+
QgsGeometry::OperationResult addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 ) /PyName=addCurvedRing/;
7277
%Docstring
73-
:rtype: int
78+
Adds a ring to polygon/multipolygon features
79+
\param ring ring to add
80+
\param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
81+
all intersecting features are tested and the ring is added to the first valid feature.
82+
\param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
83+
:return: OperationResult result code: success or reason of failure
84+
.. note::
85+
86+
available in python bindings as addCurvedRing
87+
:rtype: QgsGeometry.OperationResult
7488
%End
7589

76-
int addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
90+
QgsGeometry::OperationResult addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
7791
%Docstring
78-
:rtype: int
92+
Adds a new part polygon to a multipart feature
93+
:return:
94+
- QgsGeometry.Success
95+
- QgsGeometry.AddPartSelectedGeometryNotFound
96+
- QgsGeometry.AddPartNotMultiGeometry
97+
- QgsGeometry.InvalidBaseGeometry
98+
- QgsGeometry.InvalidInput
99+
:rtype: QgsGeometry.OperationResult
79100
%End
80101

81-
int addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
102+
QgsGeometry::OperationResult addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
82103
%Docstring
83-
:rtype: int
104+
Adds a new part polygon to a multipart feature
105+
106+
:return:
107+
- QgsGeometry.Success
108+
- QgsGeometry.AddPartSelectedGeometryNotFound
109+
- QgsGeometry.AddPartNotMultiGeometry
110+
- QgsGeometry.InvalidBaseGeometry
111+
- QgsGeometry.InvalidInput
112+
.. note::
113+
114+
available in python bindings as addPartV2
115+
:rtype: QgsGeometry.OperationResult
84116
%End
85117

86-
int addPart( QgsCurve *ring, QgsFeatureId featureId ) /PyName=addCurvedPart/;
118+
QgsGeometry::OperationResult addPart( QgsCurve *ring, QgsFeatureId featureId ) /PyName=addCurvedPart/;
87119
%Docstring
88-
:rtype: int
120+
Adds a new part polygon to a multipart feature
121+
122+
:return:
123+
- QgsGeometry.Success
124+
- QgsGeometry.AddPartSelectedGeometryNotFound
125+
- QgsGeometry.AddPartNotMultiGeometry
126+
- QgsGeometry.InvalidBaseGeometry
127+
- QgsGeometry.InvalidInput
128+
129+
.. note::
130+
131+
available in python bindings as addCurvedPart
132+
:rtype: QgsGeometry.OperationResult
89133
%End
90134

91135
int translateFeature( QgsFeatureId featureId, double dx, double dy );
@@ -98,14 +142,31 @@ class QgsVectorLayerEditUtils
98142
:rtype: int
99143
%End
100144

101-
int splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
145+
QgsGeometry::OperationResult splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
102146
%Docstring
103-
:rtype: int
147+
Splits parts cut by the given line
148+
\param splitLine line that splits the layer feature parts
149+
\param topologicalEditing true if topological editing is enabled
150+
:return:
151+
- QgsGeometry.InvalidBaseGeometry
152+
- QgsGeometry.Success
153+
- QgsGeometry.InvalidInput
154+
- QgsGeometry.NothingHappened if a selection is present but no feature has been split
155+
- QgsGeometry.InvalidBaseGeometry
156+
- QgsGeometry.GeometryEngineError
157+
- QgsGeometry.SplitCannotSplitPoint
158+
:rtype: QgsGeometry.OperationResult
104159
%End
105160

106-
int splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
161+
QgsGeometry::OperationResult splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
107162
%Docstring
108-
:rtype: int
163+
Splits features cut by the given line
164+
\param splitLine line that splits the layer features
165+
\param topologicalEditing true if topological editing is enabled
166+
:return:
167+
0 in case of success,
168+
4 if there is a selection but no feature split
169+
:rtype: QgsGeometry.OperationResult
109170
%End
110171

111172
int addTopologicalPoints( const QgsGeometry &geom );
@@ -130,15 +191,6 @@ class QgsVectorLayerEditUtils
130191
:rtype: int
131192
%End
132193

133-
protected:
134-
135-
int boundingBoxFromPointList( const QList<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const;
136-
%Docstring
137-
Little helper function that gives bounding box from a list of points.
138-
:return: 0 in case of success *
139-
:rtype: int
140-
%End
141-
142194
};
143195

144196
/************************************************************************

‎src/core/geometry/qgsgeometry.cpp‎

Lines changed: 86 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -626,35 +626,35 @@ double QgsGeometry::closestSegmentWithContext(
626626
return sqrDist;
627627
}
628628

629-
int QgsGeometry::addRing( const QList<QgsPointXY> &ring )
629+
QgsGeometry::OperationResult QgsGeometry::addRing( const QList<QgsPointXY> &ring )
630630
{
631631
detach( true );
632632

633633
QgsLineString *ringLine = new QgsLineString( ring );
634634
return addRing( ringLine );
635635
}
636636

637-
int QgsGeometry::addRing( QgsCurve *ring )
637+
QgsGeometry::OperationResult QgsGeometry::addRing( QgsCurve *ring )
638638
{
639639
if ( !d->geometry )
640640
{
641641
delete ring;
642-
return 1;
642+
return InvalidInput;
643643
}
644644

645645
detach( true );
646646

647647
return QgsGeometryEditUtils::addRing( d->geometry, ring );
648648
}
649649

650-
int QgsGeometry::addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType )
650+
QgsGeometry::OperationResult QgsGeometry::addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType )
651651
{
652652
QgsPointSequence l;
653653
convertPointList( points, l );
654654
return addPart( l, geomType );
655655
}
656656

657-
int QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType )
657+
QgsGeometry::OperationResult QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType )
658658
{
659659
QgsAbstractGeometry *partGeom = nullptr;
660660
if ( points.size() == 1 )
@@ -670,7 +670,7 @@ int QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryT
670670
return addPart( partGeom, geomType );
671671
}
672672

673-
int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType )
673+
QgsGeometry::OperationResult QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType )
674674
{
675675
if ( !d->geometry )
676676
{
@@ -687,7 +687,7 @@ int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType g
687687
d->geometry = new QgsMultiPolygonV2();
688688
break;
689689
default:
690-
return 1;
690+
return QgsGeometry::AddPartNotMultiGeometry;
691691
}
692692
}
693693
else
@@ -699,11 +699,15 @@ int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType g
699699
return QgsGeometryEditUtils::addPart( d->geometry, part );
700700
}
701701

702-
int QgsGeometry::addPart( const QgsGeometry &newPart )
702+
QgsGeometry::OperationResult QgsGeometry::addPart( const QgsGeometry &newPart )
703703
{
704-
if ( !d->geometry || !newPart.d || !newPart.d->geometry )
704+
if ( !d->geometry )
705705
{
706-
return 1;
706+
return QgsGeometry::InvalidBaseGeometry;
707+
}
708+
if ( !newPart || !newPart.d->geometry )
709+
{
710+
return QgsGeometry::AddPartNotMultiGeometry;
707711
}
708712

709713
return addPart( newPart.d->geometry->clone() );
@@ -744,11 +748,15 @@ QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
744748
}
745749
}
746750

747-
int QgsGeometry::addPart( GEOSGeometry *newPart )
751+
QgsGeometry::OperationResult QgsGeometry::addPart( GEOSGeometry *newPart )
748752
{
749-
if ( !d->geometry || !newPart )
753+
if ( !d->geometry )
750754
{
751-
return 1;
755+
return QgsGeometry::InvalidBaseGeometry;
756+
}
757+
if ( !newPart )
758+
{
759+
return QgsGeometry::AddPartNotMultiGeometry;
752760
}
753761

754762
detach( true );
@@ -757,24 +765,24 @@ int QgsGeometry::addPart( GEOSGeometry *newPart )
757765
return QgsGeometryEditUtils::addPart( d->geometry, geom );
758766
}
759767

760-
int QgsGeometry::translate( double dx, double dy )
768+
QgsGeometry::OperationResult QgsGeometry::translate( double dx, double dy )
761769
{
762770
if ( !d->geometry )
763771
{
764-
return 1;
772+
return QgsGeometry::InvalidBaseGeometry;
765773
}
766774

767775
detach( true );
768776

769777
d->geometry->transform( QTransform::fromTranslate( dx, dy ) );
770-
return 0;
778+
return QgsGeometry::Success;
771779
}
772780

773-
int QgsGeometry::rotate( double rotation, const QgsPointXY &center )
781+
QgsGeometry::OperationResult QgsGeometry::rotate( double rotation, const QgsPointXY &center )
774782
{
775783
if ( !d->geometry )
776784
{
777-
return 1;
785+
return QgsGeometry::InvalidBaseGeometry;
778786
}
779787

780788
detach( true );
@@ -783,24 +791,24 @@ int QgsGeometry::rotate( double rotation, const QgsPointXY &center )
783791
t.rotate( -rotation );
784792
t.translate( -center.x(), -center.y() );
785793
d->geometry->transform( t );
786-
return 0;
794+
return QgsGeometry::Success;
787795
}
788796

789-
int QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeometry> &newGeometries, bool topological, QList<QgsPointXY> &topologyTestPoints )
797+
QgsGeometry::OperationResult QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeometry> &newGeometries, bool topological, QList<QgsPointXY> &topologyTestPoints )
790798
{
791799
if ( !d->geometry )
792800
{
793-
return 0;
801+
return InvalidBaseGeometry;
794802
}
795803

796804
QList<QgsAbstractGeometry *> newGeoms;
797805
QgsLineString splitLineString( splitLine );
798806
QgsPointSequence tp;
799807

800808
QgsGeos geos( d->geometry );
801-
int result = geos.splitGeometry( splitLineString, newGeoms, topological, tp );
809+
QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, tp );
802810

803-
if ( result == 0 )
811+
if ( result == QgsGeometryEngine::Success )
804812
{
805813
detach( false );
806814
d->geometry = newGeoms.at( 0 );
@@ -813,27 +821,69 @@ int QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeo
813821
}
814822

815823
convertPointList( tp, topologyTestPoints );
816-
return result;
824+
825+
switch ( result )
826+
{
827+
case QgsGeometryEngine::Success:
828+
return QgsGeometry::Success;
829+
case QgsGeometryEngine::MethodNotImplemented:
830+
case QgsGeometryEngine::EngineError:
831+
case QgsGeometryEngine::NodedGeometryError:
832+
return QgsGeometry::GeometryEngineError;
833+
case QgsGeometryEngine::InvalidBaseGeometry:
834+
return QgsGeometry::InvalidBaseGeometry;
835+
case QgsGeometryEngine::InvalidInput:
836+
return QgsGeometry::InvalidInput;
837+
case QgsGeometryEngine::SplitCannotSplitPoint:
838+
return QgsGeometry::SplitCannotSplitPoint;
839+
case QgsGeometryEngine::NothingHappened:
840+
return QgsGeometry::NothingHappened;
841+
//default: do not implement default to handle properly all cases
842+
}
843+
844+
// this should never be reached
845+
Q_ASSERT( false );
846+
return QgsGeometry::NothingHappened;
817847
}
818848

819-
int QgsGeometry::reshapeGeometry( const QgsLineString &reshapeLineString )
849+
QgsGeometry::OperationResult QgsGeometry::reshapeGeometry( const QgsLineString &reshapeLineString )
820850
{
821851
if ( !d->geometry )
822852
{
823-
return 0;
853+
return InvalidBaseGeometry;
824854
}
825855

826856
QgsGeos geos( d->geometry );
827-
int errorCode = 0;
857+
QgsGeometryEngine::EngineOperationResult errorCode = QgsGeometryEngine::Success;
828858
QgsAbstractGeometry *geom = geos.reshapeGeometry( reshapeLineString, &errorCode );
829-
if ( errorCode == 0 && geom )
859+
if ( errorCode == QgsGeometryEngine::Success && geom )
830860
{
831861
detach( false );
832862
delete d->geometry;
833863
d->geometry = geom;
834-
return 0;
864+
return Success;
865+
}
866+
867+
switch ( errorCode )
868+
{
869+
case QgsGeometryEngine::Success:
870+
return Success;
871+
case QgsGeometryEngine::MethodNotImplemented:
872+
case QgsGeometryEngine::EngineError:
873+
case QgsGeometryEngine::NodedGeometryError:
874+
return GeometryEngineError;
875+
case QgsGeometryEngine::InvalidBaseGeometry:
876+
return InvalidBaseGeometry;
877+
case QgsGeometryEngine::InvalidInput:
878+
return InvalidInput;
879+
case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
880+
return GeometryEngineError;
881+
case QgsGeometryEngine::NothingHappened:
882+
return NothingHappened;
835883
}
836-
return errorCode;
884+
885+
// should not be reached
886+
return GeometryEngineError;
837887
}
838888

839889
int QgsGeometry::makeDifferenceInPlace( const QgsGeometry &other )
@@ -2091,28 +2141,28 @@ bool QgsGeometry::requiresConversionToStraightSegments() const
20912141
return d->geometry->hasCurvedSegments();
20922142
}
20932143

2094-
int QgsGeometry::transform( const QgsCoordinateTransform &ct )
2144+
QgsGeometry::OperationResult QgsGeometry::transform( const QgsCoordinateTransform &ct )
20952145
{
20962146
if ( !d->geometry )
20972147
{
2098-
return 1;
2148+
return QgsGeometry::InvalidBaseGeometry;
20992149
}
21002150

21012151
detach();
21022152
d->geometry->transform( ct );
2103-
return 0;
2153+
return QgsGeometry::Success;
21042154
}
21052155

2106-
int QgsGeometry::transform( const QTransform &ct )
2156+
QgsGeometry::OperationResult QgsGeometry::transform( const QTransform &ct )
21072157
{
21082158
if ( !d->geometry )
21092159
{
2110-
return 1;
2160+
return QgsGeometry::InvalidBaseGeometry;
21112161
}
21122162

21132163
detach();
21142164
d->geometry->transform( ct );
2115-
return 0;
2165+
return QgsGeometry::Success;
21162166
}
21172167

21182168
void QgsGeometry::mapToPixel( const QgsMapToPixel &mtp )

‎src/core/geometry/qgsgeometry.h‎

Lines changed: 287 additions & 196 deletions
Large diffs are not rendered by default.

‎src/core/geometry/qgsgeometryeditutils.cpp‎

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ email : marco.hugentobler at sourcepole dot com
2626
#include "qgsvectorlayer.h"
2727
#include <limits>
2828

29-
int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
29+
QgsGeometry::OperationResult QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *r )
3030
{
31+
std::unique_ptr<QgsCurve> ring( r );
3132
if ( !ring )
3233
{
33-
return 1;
34+
return QgsGeometry::InvalidInput;
3435
}
3536

3637
QList< QgsCurvePolygon * > polygonList;
@@ -50,23 +51,20 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
5051
}
5152
else
5253
{
53-
delete ring;
54-
return 1; //not polygon / multipolygon;
54+
return QgsGeometry::InvalidInput; //not polygon / multipolygon;
5555
}
5656

5757
//ring must be closed
5858
if ( !ring->isClosed() )
5959
{
60-
delete ring;
61-
return 2;
60+
return QgsGeometry::AddRingNotClosed;
6261
}
6362
else if ( !ring->isRing() )
6463
{
65-
delete ring;
66-
return 3;
64+
return QgsGeometry::AddRingNotValid;
6765
}
6866

69-
std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring ) );
67+
std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) );
7068
ringGeom->prepareGeometry();
7169

7270
//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 )
8179
{
8280
if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) )
8381
{
84-
delete ring;
85-
return 4;
82+
return QgsGeometry::AddRingCrossesExistingRings;
8683
}
8784
}
8885

@@ -92,59 +89,63 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
9289
if ( QgsWkbTypes::hasM( geom->wkbType() ) )
9390
ring->addMValue( 0 );
9491

95-
( *polyIter )->addInteriorRing( ring );
96-
return 0; //success
92+
( *polyIter )->addInteriorRing( ring.release() );
93+
return QgsGeometry::Success; //success
9794
}
9895
}
99-
delete ring;
100-
return 5; //not contained in any outer ring
96+
return QgsGeometry::AddRingNotInExistingFeature; //not contained in any outer ring
10197
}
10298

103-
int QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *part )
99+
QgsGeometry::OperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *p )
104100
{
101+
std::unique_ptr<QgsAbstractGeometry> part( p );
105102
if ( !geom )
106103
{
107-
return 1;
104+
return QgsGeometry::InvalidBaseGeometry;
108105
}
109106

110107
if ( !part )
111108
{
112-
return 2;
109+
return QgsGeometry::InvalidInput;
113110
}
114111

115112
//multitype?
116113
QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
117114
if ( !geomCollection )
118115
{
119-
return 1;
116+
return QgsGeometry::AddPartNotMultiGeometry;
120117
}
121118

122119
bool added = false;
123120
if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface
124121
|| QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon )
125122
{
126-
QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part );
123+
QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() );
124+
127125
if ( curve && curve->isClosed() && curve->numPoints() >= 4 )
128126
{
129-
QgsCurvePolygon *poly = nullptr;
127+
std::unique_ptr<QgsCurvePolygon> poly;
130128
if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString )
131129
{
132-
poly = new QgsPolygonV2();
130+
poly.reset( new QgsPolygonV2() );
133131
}
134132
else
135133
{
136-
poly = new QgsCurvePolygon();
134+
poly.reset( new QgsCurvePolygon() );
137135
}
136+
// Ownership is still with part, curve points to the same object and is transferred
137+
// to poly here.
138+
part.release();
138139
poly->setExteriorRing( curve );
139-
added = geomCollection->addGeometry( poly );
140+
added = geomCollection->addGeometry( poly.release() );
140141
}
141142
else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon )
142143
{
143-
added = geomCollection->addGeometry( part );
144+
added = geomCollection->addGeometry( part.release() );
144145
}
145146
else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon )
146147
{
147-
QgsGeometryCollection *parts = static_cast<QgsGeometryCollection *>( part );
148+
std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) );
148149

149150
int i;
150151
int n = geomCollection->numGeometries();
@@ -156,23 +157,19 @@ int QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometr
156157
{
157158
while ( geomCollection->numGeometries() > n )
158159
geomCollection->removeGeometry( n );
159-
delete part;
160-
return 2;
160+
return QgsGeometry::InvalidInput;
161161
}
162-
163-
delete part;
164162
}
165163
else
166164
{
167-
delete part;
168-
return 2;
165+
return QgsGeometry::InvalidInput;
169166
}
170167
}
171168
else
172169
{
173-
added = geomCollection->addGeometry( part );
170+
added = geomCollection->addGeometry( part.release() );
174171
}
175-
return added ? 0 : 2;
172+
return added ? QgsGeometry::Success : QgsGeometry::InvalidInput;
176173
}
177174

178175
bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )

‎src/core/geometry/qgsgeometryeditutils.h‎

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class QgsVectorLayer;
2424
#define SIP_NO_FILE
2525

2626
#include "qgsfeature.h"
27+
#include "qgsgeometry.h"
2728
#include <QMap>
2829

2930
/** \ingroup core
@@ -36,19 +37,24 @@ class QgsGeometryEditUtils
3637
{
3738
public:
3839

39-
/** Adds interior ring (taking ownership).
40-
\returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
41-
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/
42-
// TODO QGIS 3.0 returns an enum instead of a magic constant
43-
static int addRing( QgsAbstractGeometry *geom, QgsCurve *ring );
40+
/**
41+
* Add an interior \a ring to a \a geometry.
42+
* Ownership of the \a ring is transferred.
43+
* \returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
44+
* 3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring
45+
*/
46+
static QgsGeometry::OperationResult addRing( QgsAbstractGeometry *geometry, QgsCurve *ring SIP_TRANSFER );
4447

45-
/** Adds part to multi type geometry (taking ownership)
46-
\returns 0 in case of success, 1 if not a multigeometry, 2 if part is not a valid geometry, 3 if new polygon ring
47-
not disjoint with existing polygons of the feature*/
48-
// TODO QGIS 3.0 returns an enum instead of a magic constant
49-
static int addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *part );
48+
/**
49+
* Add a \a part to multi type \a geometry.
50+
* Ownership of the \a part is transferred.
51+
* \returns 0 in case of success, 1 if not a multigeometry, 2 if part is not a valid geometry, 3 if new polygon ring
52+
* not disjoint with existing polygons of the feature
53+
*/
54+
static QgsGeometry::OperationResult addPart( QgsAbstractGeometry *geometry, QgsAbstractGeometry *part SIP_TRANSFER );
5055

51-
/** Deletes a ring from a geometry.
56+
/**
57+
* Deletes a ring from a geometry.
5258
* \returns true if delete was successful
5359
*/
5460
static bool deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum = 0 );

‎src/core/geometry/qgsgeometryengine.h‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ email : marco.hugentobler at sourcepole dot com
1818

1919
#include "qgis_core.h"
2020
#include "qgslinestring.h"
21+
#include "qgsgeometry.h"
2122

2223
#include <QList>
2324

@@ -31,6 +32,24 @@ class QgsAbstractGeometry;
3132
class CORE_EXPORT QgsGeometryEngine
3233
{
3334
public:
35+
36+
/**
37+
* Success or failure of a geometry operation.
38+
* This gives details about cause of failure.
39+
*/
40+
enum EngineOperationResult
41+
{
42+
Success = 0, //!< Operation succeeded
43+
NothingHappened = 1000, //!< Nothing happened, without any error
44+
MethodNotImplemented, //!< Method not implemented in geometry engine
45+
EngineError, //!< Error occurred in the geometry engine
46+
NodedGeometryError, //!< Error occurred while creating a noded geometry
47+
InvalidBaseGeometry, //!< The geometry on which the operation occurs is not valid
48+
InvalidInput, //!< The input is not valid
49+
/* split */
50+
SplitCannotSplitPoint, //!< Points cannot be split
51+
};
52+
3453
virtual ~QgsGeometryEngine() = default;
3554

3655
virtual void geometryChanged() = 0;
@@ -190,17 +209,17 @@ class CORE_EXPORT QgsGeometryEngine
190209
*/
191210
virtual bool isSimple( QString *errorMsg = nullptr ) const = 0;
192211

193-
virtual int splitGeometry( const QgsLineString &splitLine,
194-
QList<QgsAbstractGeometry *> &newGeometries,
195-
bool topological,
196-
QgsPointSequence &topologyTestPoints, QString *errorMsg = nullptr ) const
212+
virtual QgsGeometryEngine::EngineOperationResult splitGeometry( const QgsLineString &splitLine,
213+
QList<QgsAbstractGeometry *> &newGeometries,
214+
bool topological,
215+
QgsPointSequence &topologyTestPoints, QString *errorMsg = nullptr ) const
197216
{
198217
Q_UNUSED( splitLine );
199218
Q_UNUSED( newGeometries );
200219
Q_UNUSED( topological );
201220
Q_UNUSED( topologyTestPoints );
202221
Q_UNUSED( errorMsg );
203-
return 2;
222+
return MethodNotImplemented;
204223
}
205224

206225
virtual QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = nullptr ) const = 0 SIP_FACTORY;

‎src/core/geometry/qgsgeos.cpp‎

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ email : marco.hugentobler at sourcepole dot com
2525
#include "qgsmultipolygon.h"
2626
#include "qgslogger.h"
2727
#include "qgspolygon.h"
28-
#include "qgsgeometry.h"
2928
#include <limits>
3029
#include <cstdio>
3130
#include <QtCore/qmath.h>
@@ -519,32 +518,32 @@ double QgsGeos::length( QString *errorMsg ) const
519518
return length;
520519
}
521520

522-
int QgsGeos::splitGeometry( const QgsLineString &splitLine,
523-
QList<QgsAbstractGeometry *> &newGeometries,
524-
bool topological,
525-
QgsPointSequence &topologyTestPoints,
526-
QString *errorMsg ) const
521+
QgsGeometryEngine::EngineOperationResult QgsGeos::splitGeometry( const QgsLineString &splitLine,
522+
QList<QgsAbstractGeometry *> &newGeometries,
523+
bool topological,
524+
QgsPointSequence &topologyTestPoints,
525+
QString *errorMsg ) const
527526
{
528527

529-
int returnCode = 0;
530-
if ( !mGeometry || !mGeos )
528+
EngineOperationResult returnCode = Success;
529+
if ( !mGeos || !mGeometry )
531530
{
532-
return 1;
531+
return InvalidBaseGeometry;
533532
}
534533

535534
//return if this type is point/multipoint
536535
if ( mGeometry->dimension() == 0 )
537536
{
538-
return 1; //cannot split points
537+
return SplitCannotSplitPoint; //cannot split points
539538
}
540539

541540
if ( !GEOSisValid_r( geosinit.ctxt, mGeos ) )
542-
return 7;
541+
return InvalidBaseGeometry;
543542

544543
//make sure splitLine is valid
545544
if ( ( mGeometry->dimension() == 1 && splitLine.numPoints() < 1 ) ||
546545
( mGeometry->dimension() == 2 && splitLine.numPoints() < 2 ) )
547-
return 1;
546+
return InvalidInput;
548547

549548
newGeometries.clear();
550549
GEOSGeometry *splitLineGeos = nullptr;
@@ -561,20 +560,22 @@ int QgsGeos::splitGeometry( const QgsLineString &splitLine,
561560
}
562561
else
563562
{
564-
return 1;
563+
return InvalidInput;
565564
}
566565

567566
if ( !GEOSisValid_r( geosinit.ctxt, splitLineGeos ) || !GEOSisSimple_r( geosinit.ctxt, splitLineGeos ) )
568567
{
569568
GEOSGeom_destroy_r( geosinit.ctxt, splitLineGeos );
570-
return 1;
569+
return InvalidInput;
571570
}
572571

573572
if ( topological )
574573
{
575574
//find out candidate points for topological corrections
576-
if ( topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) != 0 )
577-
return 1;
575+
if ( !topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) )
576+
{
577+
return InvalidInput; // TODO: is it really an invalid input?
578+
}
578579
}
579580

580581
//call split function depending on geometry type
@@ -590,33 +591,33 @@ int QgsGeos::splitGeometry( const QgsLineString &splitLine,
590591
}
591592
else
592593
{
593-
return 1;
594+
return InvalidInput;
594595
}
595596
}
596-
CATCH_GEOS_WITH_ERRMSG( 2 )
597+
CATCH_GEOS_WITH_ERRMSG( EngineError )
597598

598599
return returnCode;
599600
}
600601

601602

602603

603-
int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg ) const
604+
bool QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg ) const
604605
{
605606
//Find out the intersection points between splitLineGeos and this geometry.
606607
//These points need to be tested for topological correctness by the calling function
607608
//if topological editing is enabled
608609

609610
if ( !mGeos )
610611
{
611-
return 1;
612+
return false;
612613
}
613614

614615
try
615616
{
616617
testPoints.clear();
617618
GEOSGeometry *intersectionGeom = GEOSIntersection_r( geosinit.ctxt, mGeos, splitLine );
618619
if ( !intersectionGeom )
619-
return 1;
620+
return false;
620621

621622
bool simple = false;
622623
int nIntersectGeoms = 1;
@@ -656,7 +657,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint
656657
}
657658
CATCH_GEOS_WITH_ERRMSG( 1 )
658659

659-
return 0;
660+
return true;
660661
}
661662

662663
GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const
@@ -725,22 +726,22 @@ GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const
725726
return asGeos( &lines, mPrecision );
726727
}
727728

728-
int QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const
729+
QgsGeometryEngine::EngineOperationResult QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const
729730
{
730731
if ( !splitLine )
731-
return 2;
732+
return InvalidInput;
732733

733734
if ( !mGeos )
734-
return 5;
735+
return InvalidBaseGeometry;
735736

736737
//first test if linestring intersects geometry. If not, return straight away
737738
if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) )
738-
return 1;
739+
return NothingHappened;
739740

740741
//check that split line has no linear intersection
741742
int linearIntersect = GEOSRelatePattern_r( geosinit.ctxt, mGeos, splitLine, "1********" );
742743
if ( linearIntersect > 0 )
743-
return 3;
744+
return InvalidInput;
744745

745746
int splitGeomType = GEOSGeomTypeId_r( geosinit.ctxt, splitLine );
746747

@@ -778,25 +779,25 @@ int QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeom
778779
}
779780

780781
GEOSGeom_destroy_r( geosinit.ctxt, splitGeom );
781-
return 0;
782+
return Success;
782783
}
783784

784-
int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const
785+
QgsGeometryEngine::EngineOperationResult QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const
785786
{
786787
if ( !splitLine )
787-
return 2;
788+
return InvalidInput;
788789

789790
if ( !mGeos )
790-
return 5;
791+
return InvalidBaseGeometry;
791792

792793
//first test if linestring intersects geometry. If not, return straight away
793794
if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) )
794-
return 1;
795+
return NothingHappened;
795796

796797
//first union all the polygon rings together (to get them noded, see JTS developer guide)
797798
GEOSGeometry *nodedGeometry = nodeGeometries( splitLine, mGeos );
798799
if ( !nodedGeometry )
799-
return 2; //an error occurred during noding
800+
return NodedGeometryError; //an error occurred during noding
800801

801802
GEOSGeometry *polygons = GEOSPolygonize_r( geosinit.ctxt, &nodedGeometry, 1 );
802803
if ( !polygons || numberOfGeometries( polygons ) == 0 )
@@ -806,7 +807,7 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
806807

807808
GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
808809

809-
return 4;
810+
return InvalidBaseGeometry;
810811
}
811812

812813
GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
@@ -859,7 +860,7 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
859860
{
860861
GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] );
861862
}
862-
return 1;
863+
return NothingHappened;
863864
}
864865

865866
int i;
@@ -871,13 +872,13 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
871872
for ( i = 0; i < testedGeometries.size(); ++i )
872873
GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] );
873874

874-
return 3;
875+
return InvalidBaseGeometry;
875876
}
876877

877878
for ( i = 0; i < testedGeometries.size(); ++i )
878879
newGeometries << fromGeos( testedGeometries[i] );
879880

880-
return 0;
881+
return Success;
881882
}
882883

883884
GEOSGeometry *QgsGeos::nodeGeometries( const GEOSGeometry *splitLine, const GEOSGeometry *geom )
@@ -1892,11 +1893,17 @@ QgsAbstractGeometry *QgsGeos::singleSidedBuffer( double distance, int segments,
18921893
return fromGeos( geos.get() );
18931894
}
18941895

1895-
QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithLine, int *errorCode, QString *errorMsg ) const
1896+
QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg ) const
18961897
{
1897-
if ( !mGeos || reshapeWithLine.numPoints() < 2 || mGeometry->dimension() == 0 )
1898+
if ( !mGeos || mGeometry->dimension() == 0 )
1899+
{
1900+
if ( errorCode ) { *errorCode = InvalidBaseGeometry; }
1901+
return nullptr;
1902+
}
1903+
1904+
if ( reshapeWithLine.numPoints() < 2 )
18981905
{
1899-
if ( errorCode ) { *errorCode = 1; }
1906+
if ( errorCode ) { *errorCode = InvalidInput; }
19001907
return nullptr;
19011908
}
19021909

@@ -1906,7 +1913,7 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
19061913
int numGeoms = GEOSGetNumGeometries_r( geosinit.ctxt, mGeos );
19071914
if ( numGeoms == -1 )
19081915
{
1909-
if ( errorCode ) { *errorCode = 1; }
1916+
if ( errorCode ) { *errorCode = InvalidBaseGeometry; }
19101917
GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
19111918
return nullptr;
19121919
}
@@ -1930,7 +1937,8 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
19301937
reshapedGeometry = reshapePolygon( mGeos, reshapeLineGeos, mPrecision );
19311938
}
19321939

1933-
if ( errorCode ) { *errorCode = 0; }
1940+
if ( errorCode )
1941+
*errorCode = Success;
19341942
QgsAbstractGeometry *reshapeResult = fromGeos( reshapedGeometry );
19351943
GEOSGeom_destroy_r( geosinit.ctxt, reshapedGeometry );
19361944
GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
@@ -1978,21 +1986,22 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
19781986
delete[] newGeoms;
19791987
if ( !newMultiGeom )
19801988
{
1981-
if ( errorCode ) { *errorCode = 3; }
1989+
if ( errorCode ) { *errorCode = EngineError; }
19821990
return nullptr;
19831991
}
19841992

19851993
if ( reshapeTookPlace )
19861994
{
1987-
if ( errorCode ) { *errorCode = 0; }
1995+
if ( errorCode )
1996+
*errorCode = Success;
19881997
QgsAbstractGeometry *reshapedMultiGeom = fromGeos( newMultiGeom );
19891998
GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom );
19901999
return reshapedMultiGeom;
19912000
}
19922001
else
19932002
{
19942003
GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom );
1995-
if ( errorCode ) { *errorCode = 1; }
2004+
if ( errorCode ) { *errorCode = NothingHappened; }
19962005
return nullptr;
19972006
}
19982007
}

‎src/core/geometry/qgsgeos.h‎

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com
2020

2121
#include "qgis_core.h"
2222
#include "qgsgeometryengine.h"
23+
#include "qgsgeometry.h"
2324
#include <geos_c.h>
2425

2526
class QgsLineString;
@@ -106,11 +107,11 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
106107
\param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset
107108
\param[out] errorMsg error messages emitted, if any
108109
\returns 0 in case of success, 1 if geometry has not been split, error else*/
109-
int splitGeometry( const QgsLineString &splitLine,
110-
QList<QgsAbstractGeometry *> &newGeometries,
111-
bool topological,
112-
QgsPointSequence &topologyTestPoints,
113-
QString *errorMsg = nullptr ) const override;
110+
EngineOperationResult splitGeometry( const QgsLineString &splitLine,
111+
QList<QgsAbstractGeometry *> &newGeometries,
112+
bool topological,
113+
QgsPointSequence &topologyTestPoints,
114+
QString *errorMsg = nullptr ) const override;
114115

115116
QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = nullptr ) const override;
116117

@@ -131,8 +132,14 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
131132
int joinStyle, double miterLimit,
132133
QString *errorMsg = nullptr ) const;
133134

134-
135-
QgsAbstractGeometry *reshapeGeometry( const QgsLineString &reshapeWithLine, int *errorCode, QString *errorMsg = nullptr ) const;
135+
/**
136+
* Reshapes the geometry using a line
137+
* @param reshapeWithLine the line used to reshape lines or polygons
138+
* @param errorCode if specified, provides result of operation (success or reason of failure)
139+
* @param errorMsg if specified, provides more details about failure
140+
* @return the reshaped geometry
141+
*/
142+
QgsAbstractGeometry *reshapeGeometry( const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg = nullptr ) const;
136143

137144
/** Merges any connected lines in a LineString/MultiLineString geometry and
138145
* converts them to single line strings.
@@ -261,10 +268,10 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
261268
static GEOSGeometry *createGeosPolygon( const QgsAbstractGeometry *poly, double precision );
262269

263270
//utils for geometry split
264-
int topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg = nullptr ) const;
271+
bool topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg = nullptr ) const;
265272
GEOSGeometry *linePointDifference( GEOSGeometry *GEOSsplitPoint ) const;
266-
int splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
267-
int splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
273+
EngineOperationResult splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
274+
EngineOperationResult splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
268275

269276
//utils for reshape
270277
static GEOSGeometry *reshapeLine( const GEOSGeometry *line, const GEOSGeometry *reshapeLineGeos, double precision );

‎src/core/qgsvectorlayereditutils.cpp‎

Lines changed: 95 additions & 114 deletions
Large diffs are not rendered by default.

‎src/core/qgsvectorlayereditutils.h‎

Lines changed: 106 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919
#include "qgis_core.h"
2020
#include "qgis.h"
2121
#include "qgsfeature.h"
22-
23-
#include "qgsvectorlayer.h"
2422
#include "qgsgeometry.h"
23+
#include "qgsvectorlayer.h"
2524

2625
class QgsCurve;
2726

@@ -33,138 +32,146 @@ class CORE_EXPORT QgsVectorLayerEditUtils
3332
public:
3433
QgsVectorLayerEditUtils( QgsVectorLayer *layer );
3534

36-
/** Insert a new vertex before the given vertex number,
37-
* in the given ring, item (first number is index 0), and feature
38-
* Not meaningful for Point geometries
35+
/**
36+
* Insert a new vertex before the given vertex number,
37+
* in the given ring, item (first number is index 0), and feature
38+
* Not meaningful for Point geometries
3939
*/
4040
bool insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex );
4141

42-
/** Insert a new vertex before the given vertex number,
43-
* in the given ring, item (first number is index 0), and feature
44-
* Not meaningful for Point geometries
42+
/**
43+
* Inserts a new vertex before the given vertex number,
44+
* in the given ring, item (first number is index 0), and feature
45+
* Not meaningful for Point geometries
4546
*/
4647
bool insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex );
4748

48-
/** Moves the vertex at the given position number,
49-
* ring and item (first number is index 0), and feature
50-
* to the given coordinates
49+
/**
50+
* Moves the vertex at the given position number,
51+
* ring and item (first number is index 0), and feature
52+
* to the given coordinates
5153
*/
5254
bool moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex );
5355

54-
/** Moves the vertex at the given position number,
55-
* ring and item (first number is index 0), and feature
56-
* to the given coordinates
57-
* \note available in Python bindings as moveVertexV2
56+
/**
57+
* Moves the vertex at the given position number,
58+
* ring and item (first number is index 0), and feature
59+
* to the given coordinates
60+
* \note available in Python bindings as moveVertexV2
5861
*/
5962
bool moveVertex( const QgsPoint &p, QgsFeatureId atFeatureId, int atVertex ) SIP_PYNAME( moveVertexV2 );
6063

61-
/** Deletes a vertex from a feature.
64+
/**
65+
* Deletes a vertex from a feature.
6266
* \param featureId ID of feature to remove vertex from
6367
* \param vertex index of vertex to delete
6468
* \since QGIS 2.14
6569
*/
6670
QgsVectorLayer::EditResult deleteVertex( QgsFeatureId featureId, int vertex );
6771

68-
/** Adds a ring to polygon/multipolygon features
72+
/**
73+
* Adds a ring to polygon/multipolygon features
6974
* \param ring ring to add
7075
* \param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
7176
* all intersecting features are tested and the ring is added to the first valid feature.
7277
* \param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
73-
* \returns
74-
* 0 in case of success,
75-
* 1 problem with feature type,
76-
* 2 ring not closed,
77-
* 3 ring not valid,
78-
* 4 ring crosses existing rings,
79-
* 5 no feature found where ring can be inserted
80-
*/
81-
// TODO QGIS 3.0 returns an enum instead of a magic constant
82-
int addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr );
83-
84-
/** Adds a ring to polygon/multipolygon features
78+
* \return OperationResult result code: success or reason of failure
79+
*/
80+
QgsGeometry::OperationResult addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr );
81+
82+
/**
83+
* Adds a ring to polygon/multipolygon features
8584
* \param ring ring to add
8685
* \param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
8786
* all intersecting features are tested and the ring is added to the first valid feature.
8887
* \param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
89-
* \returns
90-
* 0 in case of success,
91-
* 1 problem with feature type,
92-
* 2 ring not closed,
93-
* 3 ring not valid,
94-
* 4 ring crosses existing rings,
95-
* 5 no feature found where ring can be inserted
96-
* \note available in Python bindings as addCurvedRing
97-
*/
98-
// TODO QGIS 3.0 returns an enum instead of a magic constant
99-
int addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ) SIP_PYNAME( addCurvedRing );
100-
101-
/** Adds a new part polygon to a multipart feature
102-
* \returns
103-
* 0 in case of success,
104-
* 1 if selected feature is not multipart,
105-
* 2 if ring is not a valid geometry,
106-
* 3 if new polygon ring not disjoint with existing rings,
107-
* 4 if no feature was selected,
108-
* 5 if several features are selected,
109-
* 6 if selected geometry not found
110-
*/
111-
// TODO QGIS 3.0 returns an enum instead of a magic constant
112-
int addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
113-
114-
/** Adds a new part polygon to a multipart feature
115-
* \returns
116-
* 0 in case of success,
117-
* 1 if selected feature is not multipart,
118-
* 2 if ring is not a valid geometry,
119-
* 3 if new polygon ring not disjoint with existing rings,
120-
* 4 if no feature was selected,
121-
* 5 if several features are selected,
122-
* 6 if selected geometry not found
123-
* \note available in Python bindings as addPartV2
124-
*/
125-
// TODO QGIS 3.0 returns an enum instead of a magic constant
126-
int addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
127-
128-
//! \note available in Python bindings as addCurvedPart
129-
// TODO QGIS 3.0 returns an enum instead of a magic constant
130-
int addPart( QgsCurve *ring, QgsFeatureId featureId ) SIP_PYNAME( addCurvedPart );
131-
132-
/** Translates feature by dx, dy
88+
* \return OperationResult result code: success or reason of failure
89+
* \note available in python bindings as addCurvedRing
90+
*/
91+
QgsGeometry::OperationResult addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ) SIP_PYNAME( addCurvedRing );
92+
93+
/**
94+
* Adds a new part polygon to a multipart feature
95+
* \return
96+
* - QgsGeometry::Success
97+
* - QgsGeometry::AddPartSelectedGeometryNotFound
98+
* - QgsGeometry::AddPartNotMultiGeometry
99+
* - QgsGeometry::InvalidBaseGeometry
100+
* - QgsGeometry::InvalidInput
101+
*/
102+
QgsGeometry::OperationResult addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
103+
104+
/**
105+
* Adds a new part polygon to a multipart feature
106+
*
107+
* \return
108+
* - QgsGeometry::Success
109+
* - QgsGeometry::AddPartSelectedGeometryNotFound
110+
* - QgsGeometry::AddPartNotMultiGeometry
111+
* - QgsGeometry::InvalidBaseGeometry
112+
* - QgsGeometry::InvalidInput
113+
* \note available in python bindings as addPartV2
114+
*/
115+
QgsGeometry::OperationResult addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
116+
117+
/**
118+
* Adds a new part polygon to a multipart feature
119+
*
120+
* \return
121+
* - QgsGeometry::Success
122+
* - QgsGeometry::AddPartSelectedGeometryNotFound
123+
* - QgsGeometry::AddPartNotMultiGeometry
124+
* - QgsGeometry::InvalidBaseGeometry
125+
* - QgsGeometry::InvalidInput
126+
*
127+
* \note available in python bindings as addCurvedPart
128+
*/
129+
QgsGeometry::OperationResult addPart( QgsCurve *ring, QgsFeatureId featureId ) SIP_PYNAME( addCurvedPart );
130+
131+
/**
132+
* Translates feature by dx, dy
133133
* \param featureId id of the feature to translate
134134
* \param dx translation of x-coordinate
135135
* \param dy translation of y-coordinate
136-
* \returns 0 in case of success
136+
* \return 0 in case of success
137137
*/
138138
int translateFeature( QgsFeatureId featureId, double dx, double dy );
139139

140-
/** Splits parts cut by the given line
141-
* \param splitLine line that splits the layer feature parts
142-
* \param topologicalEditing true if topological editing is enabled
143-
* \returns
144-
* 0 in case of success,
145-
* 4 if there is a selection but no feature split
140+
/**
141+
* Splits parts cut by the given line
142+
* \param splitLine line that splits the layer feature parts
143+
* \param topologicalEditing true if topological editing is enabled
144+
* \return
145+
* - QgsGeometry::InvalidBaseGeometry
146+
* - QgsGeometry::Success
147+
* - QgsGeometry::InvalidInput
148+
* - QgsGeometry::NothingHappened if a selection is present but no feature has been split
149+
* - QgsGeometry::InvalidBaseGeometry
150+
* - QgsGeometry::GeometryEngineError
151+
* - QgsGeometry::SplitCannotSplitPoint
146152
*/
147-
// TODO QGIS 3.0 returns an enum instead of a magic constant
148-
int splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
153+
QgsGeometry::OperationResult splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
149154

150-
/** Splits features cut by the given line
151-
* \param splitLine line that splits the layer features
152-
* \param topologicalEditing true if topological editing is enabled
153-
* \returns
154-
* 0 in case of success,
155-
* 4 if there is a selection but no feature split
155+
/**
156+
* Splits features cut by the given line
157+
* \param splitLine line that splits the layer features
158+
* \param topologicalEditing true if topological editing is enabled
159+
* \return
160+
* 0 in case of success,
161+
* 4 if there is a selection but no feature split
156162
*/
157-
// TODO QGIS 3.0 returns an enum instead of a magic constant
158-
int splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
163+
QgsGeometry::OperationResult splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
159164

160-
/** Adds topological points for every vertex of the geometry.
165+
/**
166+
* Adds topological points for every vertex of the geometry.
161167
* \param geom the geometry where each vertex is added to segments of other features
162168
* \note geom is not going to be modified by the function
163169
* \returns 0 in case of success
164170
*/
165171
int addTopologicalPoints( const QgsGeometry &geom );
166172

167-
/** Adds a vertex to segments which intersect point p but don't
173+
/**
174+
* Adds a vertex to segments which intersect point p but don't
168175
* already have a vertex there. If a feature already has a vertex at position p,
169176
* no additional vertex is inserted. This method is useful for topological
170177
* editing.
@@ -173,15 +180,15 @@ class CORE_EXPORT QgsVectorLayerEditUtils
173180
*/
174181
int addTopologicalPoints( const QgsPointXY &p );
175182

176-
protected:
177-
178-
/** Little helper function that gives bounding box from a list of points.
179-
\returns 0 in case of success */
180-
int boundingBoxFromPointList( const QList<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const;
181-
182183
private:
183184

184-
QgsVectorLayer *L = nullptr;
185+
/**
186+
* Little helper function that gives bounding box from a list of points.
187+
* \returns True in case of success
188+
*/
189+
bool boundingBoxFromPointList( const QList<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const;
190+
191+
QgsVectorLayer *mLayer = nullptr;
185192
};
186193

187194
#endif // QGSVECTORLAYEREDITUTILS_H

‎tests/src/python/test_qgsgeometry.py‎

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,14 +1423,14 @@ def testAddPart(self):
14231423
]
14241424

14251425
polyline = QgsGeometry.fromPolyline(points[0])
1426-
self.assertEqual(polyline.addPoints(points[1][0:1]), 2, "addPoints with one point line unexpectedly succeeded.")
1427-
self.assertEqual(polyline.addPoints(points[1][0:2]), 0, "addPoints with two point line failed.")
1426+
self.assertEqual(polyline.addPoints(points[1][0:1]), QgsGeometry.InvalidInput, "addPoints with one point line unexpectedly succeeded.")
1427+
self.assertEqual(polyline.addPoints(points[1][0:2]), QgsGeometry.Success, "addPoints with two point line failed.")
14281428
expwkt = "MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 0, 3 1))"
14291429
wkt = polyline.exportToWkt()
14301430
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
14311431

14321432
polyline = QgsGeometry.fromPolyline(points[0])
1433-
self.assertEqual(polyline.addPoints(points[1]), 0, "addPoints with %d point line failed." % len(points[1]))
1433+
self.assertEqual(polyline.addPoints(points[1]), QgsGeometry.Success, "addPoints with %d point line failed." % len(points[1]))
14341434
expwkt = "MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 0, 3 1, 5 1, 5 0, 6 0))"
14351435
wkt = polyline.exportToWkt()
14361436
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@@ -1439,7 +1439,7 @@ def testAddPart(self):
14391439
polyline = QgsGeometry.fromPolyline(points[0])
14401440
polyline.geometry().addZValue(4.0)
14411441
points2 = [QgsPoint(p[0], p[1], 3.0, wkbType=QgsWkbTypes.PointZ) for p in points[1]]
1442-
self.assertEqual(polyline.addPointsV2(points2), 0)
1442+
self.assertEqual(polyline.addPointsV2(points2), QgsGeometry.Success)
14431443
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))"
14441444
wkt = polyline.exportToWkt()
14451445
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@@ -1456,34 +1456,34 @@ def testAddPart(self):
14561456

14571457
polygon = QgsGeometry.fromPolygon(points[0])
14581458

1459-
self.assertEqual(polygon.addPoints(points[1][0][0:1]), 2, "addPoints with one point ring unexpectedly succeeded.")
1460-
self.assertEqual(polygon.addPoints(points[1][0][0:2]), 2, "addPoints with two point ring unexpectedly succeeded.")
1461-
self.assertEqual(polygon.addPoints(points[1][0][0:3]), 2, "addPoints with unclosed three point ring unexpectedly succeeded.")
1462-
self.assertEqual(polygon.addPoints([QgsPointXY(4, 0), QgsPointXY(5, 0), QgsPointXY(4, 0)]), 2, "addPoints with 'closed' three point ring unexpectedly succeeded.")
1459+
self.assertEqual(polygon.addPoints(points[1][0][0:1]), QgsGeometry.InvalidInput, "addPoints with one point ring unexpectedly succeeded.")
1460+
self.assertEqual(polygon.addPoints(points[1][0][0:2]), QgsGeometry.InvalidInput, "addPoints with two point ring unexpectedly succeeded.")
1461+
self.assertEqual(polygon.addPoints(points[1][0][0:3]), QgsGeometry.InvalidInput, "addPoints with unclosed three point ring unexpectedly succeeded.")
1462+
self.assertEqual(polygon.addPoints([QgsPointXY(4, 0), QgsPointXY(5, 0), QgsPointXY(4, 0)]), QgsGeometry.InvalidInput, "addPoints with 'closed' three point ring unexpectedly succeeded.")
14631463

1464-
self.assertEqual(polygon.addPoints(points[1][0]), 0, "addPoints failed")
1464+
self.assertEqual(polygon.addPoints(points[1][0]), QgsGeometry.Success, "addPoints failed")
14651465
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)))"
14661466
wkt = polygon.exportToWkt()
14671467
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
14681468

14691469
mp = QgsGeometry.fromMultiPolygon(points[:1])
14701470
p = QgsGeometry.fromPolygon(points[1])
14711471

1472-
self.assertEqual(mp.addPartGeometry(p), 0)
1472+
self.assertEqual(mp.addPartGeometry(p), QgsGeometry.Success)
14731473
wkt = mp.exportToWkt()
14741474
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
14751475

14761476
mp = QgsGeometry.fromMultiPolygon(points[:1])
14771477
mp2 = QgsGeometry.fromMultiPolygon(points[1:])
1478-
self.assertEqual(mp.addPartGeometry(mp2), 0)
1478+
self.assertEqual(mp.addPartGeometry(mp2), QgsGeometry.Success)
14791479
wkt = mp.exportToWkt()
14801480
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
14811481

14821482
# test adding a part with Z values
14831483
polygon = QgsGeometry.fromPolygon(points[0])
14841484
polygon.geometry().addZValue(4.0)
14851485
points2 = [QgsPoint(pi[0], pi[1], 3.0, wkbType=QgsWkbTypes.PointZ) for pi in points[1][0]]
1486-
self.assertEqual(polygon.addPointsV2(points2), 0)
1486+
self.assertEqual(polygon.addPointsV2(points2), QgsGeometry.Success)
14871487
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)))"
14881488
wkt = polygon.exportToWkt()
14891489
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@@ -1492,38 +1492,38 @@ def testAddPart(self):
14921492
empty = QgsGeometry()
14931493
# if not default type specified, addPart should fail
14941494
result = empty.addPoints([QgsPointXY(4, 0)])
1495-
assert result != 0, 'Got return code {}'.format(result)
1495+
assert result != QgsGeometry.Success, 'Got return code {}'.format(result)
14961496
result = empty.addPoints([QgsPointXY(4, 0)], QgsWkbTypes.PointGeometry)
1497-
self.assertEqual(result, 0, 'Got return code {}'.format(result))
1497+
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
14981498
wkt = empty.exportToWkt()
14991499
expwkt = 'MultiPoint ((4 0))'
15001500
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
15011501
result = empty.addPoints([QgsPointXY(5, 1)])
1502-
self.assertEqual(result, 0, 'Got return code {}'.format(result))
1502+
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
15031503
wkt = empty.exportToWkt()
15041504
expwkt = 'MultiPoint ((4 0),(5 1))'
15051505
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
15061506
# next try with lines
15071507
empty = QgsGeometry()
15081508
result = empty.addPoints(points[0][0], QgsWkbTypes.LineGeometry)
1509-
self.assertEqual(result, 0, 'Got return code {}'.format(result))
1509+
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
15101510
wkt = empty.exportToWkt()
15111511
expwkt = 'MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0))'
15121512
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
15131513
result = empty.addPoints(points[1][0])
1514-
self.assertEqual(result, 0, 'Got return code {}'.format(result))
1514+
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
15151515
wkt = empty.exportToWkt()
15161516
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))'
15171517
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
15181518
# finally try with polygons
15191519
empty = QgsGeometry()
15201520
result = empty.addPoints(points[0][0], QgsWkbTypes.PolygonGeometry)
1521-
self.assertEqual(result, 0, 'Got return code {}'.format(result))
1521+
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
15221522
wkt = empty.exportToWkt()
15231523
expwkt = 'MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)))'
15241524
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
15251525
result = empty.addPoints(points[1][0])
1526-
self.assertEqual(result, 0, 'Got return code {}'.format(result))
1526+
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
15271527
wkt = empty.exportToWkt()
15281528
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)))'
15291529
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)

0 commit comments

Comments
 (0)
Please sign in to comment.