Skip to content

Commit c05aa14

Browse files
elpasonyalldawson
authored andcommittedSep 14, 2018
Test supportInPlace for all polygon algorithms
1 parent d4af8ad commit c05aa14

18 files changed

+234
-5
lines changed
 

‎python/gui/auto_generated/processing/qgsprocessingtoolboxmodel.sip.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,11 @@ Returns any filters that affect how toolbox content is filtered.
419419
%End
420420

421421
void setInPlaceLayer( QgsVectorLayer *layer );
422+
%Docstring
423+
Sets geometry \type for the in-place algorithms
424+
425+
:param layer: the vector layer for in-place algorithm filter
426+
%End
422427

423428
void setFilterString( const QString &filter );
424429
%Docstring

‎src/analysis/processing/qgsalgorithmdrape.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
***************************************************************************/
1717

1818
#include "qgsalgorithmdrape.h"
19+
#include "qgsvectorlayer.h"
1920

2021
///@cond PRIVATE
2122

@@ -195,6 +196,13 @@ QgsDrapeToZAlgorithm *QgsDrapeToZAlgorithm::createInstance() const
195196
return new QgsDrapeToZAlgorithm();
196197
}
197198

199+
bool QgsDrapeToZAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
200+
{
201+
if ( ! QgsDrapeAlgorithmBase::supportInPlaceEdit( layer ) )
202+
return false;
203+
return QgsWkbTypes::hasZ( layer->wkbType() );
204+
}
205+
198206
QgsWkbTypes::Type QgsDrapeToZAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const
199207
{
200208
QgsWkbTypes::Type wkb = inputWkbType;
@@ -247,6 +255,13 @@ QgsDrapeToMAlgorithm *QgsDrapeToMAlgorithm::createInstance() const
247255
return new QgsDrapeToMAlgorithm();
248256
}
249257

258+
bool QgsDrapeToMAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
259+
{
260+
if ( ! QgsDrapeAlgorithmBase::supportInPlaceEdit( layer ) )
261+
return false;
262+
return QgsWkbTypes::hasM( layer->wkbType() );
263+
}
264+
250265
QgsWkbTypes::Type QgsDrapeToMAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const
251266
{
252267
QgsWkbTypes::Type wkb = inputWkbType;

‎src/analysis/processing/qgsalgorithmdrape.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class QgsDrapeToZAlgorithm : public QgsDrapeAlgorithmBase
7373
QString shortHelpString() const override;
7474
QString shortDescription() const override;
7575
QgsDrapeToZAlgorithm *createInstance() const override SIP_FACTORY;
76+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
7677

7778
protected:
7879

@@ -82,6 +83,7 @@ class QgsDrapeToZAlgorithm : public QgsDrapeAlgorithmBase
8283
void prepareGeometry( QgsGeometry &geometry, double defaultVal ) const override;
8384
QgsPoint drapeVertex( const QgsPoint &vertex, double rasterVal ) const override;
8485

86+
8587
};
8688

8789

@@ -95,6 +97,7 @@ class QgsDrapeToMAlgorithm : public QgsDrapeAlgorithmBase
9597
QString shortHelpString() const override;
9698
QString shortDescription() const override;
9799
QgsDrapeToMAlgorithm *createInstance() const override SIP_FACTORY;
100+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
98101

99102
protected:
100103

‎src/analysis/processing/qgsalgorithmfixgeometries.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
***************************************************************************/
1717

1818
#include "qgsalgorithmfixgeometries.h"
19+
#include "qgsvectorlayer.h"
1920

2021
///@cond PRIVATE
2122

@@ -72,6 +73,14 @@ QgsFixGeometriesAlgorithm *QgsFixGeometriesAlgorithm::createInstance() const
7273
return new QgsFixGeometriesAlgorithm();
7374
}
7475

76+
bool QgsFixGeometriesAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
77+
{
78+
if ( ! QgsProcessingFeatureBasedAlgorithm::supportInPlaceEdit( layer ) )
79+
return false;
80+
// The algorithm would drop M, so disable it if the layer has M
81+
return ! QgsWkbTypes::hasM( layer->wkbType() );
82+
}
83+
7584
QgsFeatureList QgsFixGeometriesAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback )
7685
{
7786
if ( !feature.hasGeometry() )

‎src/analysis/processing/qgsalgorithmfixgeometries.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class QgsFixGeometriesAlgorithm : public QgsProcessingFeatureBasedAlgorithm
4141
QString groupId() const override;
4242
QString shortHelpString() const override;
4343
QgsFixGeometriesAlgorithm *createInstance() const override SIP_FACTORY;
44+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
4445

4546
protected:
4647
QgsProcessingFeatureSource::Flag sourceFlags() const override;

‎src/analysis/processing/qgsalgorithmminimumenclosingcircle.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
***************************************************************************/
1717

1818
#include "qgsalgorithmminimumenclosingcircle.h"
19+
#include "qgsvectorlayer.h"
1920

2021
///@cond PRIVATE
2122

@@ -51,7 +52,7 @@ QString QgsMinimumEnclosingCircleAlgorithm::outputName() const
5152

5253
QgsWkbTypes::Type QgsMinimumEnclosingCircleAlgorithm::outputWkbType( QgsWkbTypes::Type ) const
5354
{
54-
return QgsWkbTypes::Polygon;
55+
return QgsWkbTypes::Type::Polygon;
5556
}
5657

5758
void QgsMinimumEnclosingCircleAlgorithm::initParameters( const QVariantMap & )
@@ -72,6 +73,14 @@ QgsMinimumEnclosingCircleAlgorithm *QgsMinimumEnclosingCircleAlgorithm::createIn
7273
return new QgsMinimumEnclosingCircleAlgorithm();
7374
}
7475

76+
bool QgsMinimumEnclosingCircleAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
77+
{
78+
if ( ! QgsProcessingFeatureBasedAlgorithm::supportInPlaceEdit( layer ) )
79+
return false;
80+
// (no Z no M)
81+
return !( QgsWkbTypes::hasM( layer->wkbType() ) || QgsWkbTypes::hasZ( layer->wkbType() ) );
82+
}
83+
7584
QgsFields QgsMinimumEnclosingCircleAlgorithm::outputFields( const QgsFields &inputFields ) const
7685
{
7786
QgsFields fields = inputFields;

‎src/analysis/processing/qgsalgorithmminimumenclosingcircle.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class QgsMinimumEnclosingCircleAlgorithm : public QgsProcessingFeatureBasedAlgor
4242
QString groupId() const override;
4343
QString shortHelpString() const override;
4444
QgsMinimumEnclosingCircleAlgorithm *createInstance() const override SIP_FACTORY;
45+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
4546

4647
protected:
4748
QString outputName() const override;
@@ -53,6 +54,7 @@ class QgsMinimumEnclosingCircleAlgorithm : public QgsProcessingFeatureBasedAlgor
5354
private:
5455

5556
int mSegments = 72;
57+
5658
};
5759

5860
///@endcond PRIVATE

‎src/analysis/processing/qgsalgorithmmultiparttosinglepart.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
***************************************************************************/
1717

1818
#include "qgsalgorithmmultiparttosinglepart.h"
19+
#include "qgsvectorlayer.h"
1920

2021
///@cond PRIVATE
2122

@@ -66,6 +67,14 @@ QgsMultipartToSinglepartAlgorithm *QgsMultipartToSinglepartAlgorithm::createInst
6667
return new QgsMultipartToSinglepartAlgorithm();
6768
}
6869

70+
bool QgsMultipartToSinglepartAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
71+
{
72+
if ( ! QgsProcessingFeatureBasedAlgorithm::supportInPlaceEdit( layer ) )
73+
return false;
74+
// The layer is already single part!
75+
return QgsWkbTypes::isMultiType( layer->wkbType() );
76+
}
77+
6978
QgsProcessingFeatureSource::Flag QgsMultipartToSinglepartAlgorithm::sourceFlags() const
7079
{
7180
// skip geometry checks - this algorithm can be used to repair geometries

‎src/analysis/processing/qgsalgorithmmultiparttosinglepart.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class QgsMultipartToSinglepartAlgorithm : public QgsProcessingFeatureBasedAlgori
4545
QString groupId() const override;
4646
QString shortHelpString() const override;
4747
QgsMultipartToSinglepartAlgorithm *createInstance() const override SIP_FACTORY;
48+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
4849

4950
protected:
5051

‎src/analysis/processing/qgsalgorithmmultiringconstantbuffer.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
***************************************************************************/
1717

1818
#include "qgsalgorithmmultiringconstantbuffer.h"
19+
#include "qgsvectorlayer.h"
1920

2021
///@cond PRIVATE
2122

@@ -77,6 +78,14 @@ void QgsMultiRingConstantBufferAlgorithm::initParameters( const QVariantMap & )
7778
addParameter( distance.release() );
7879
}
7980

81+
bool QgsMultiRingConstantBufferAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
82+
{
83+
if ( ! QgsProcessingFeatureBasedAlgorithm::supportInPlaceEdit( layer ) )
84+
return false;
85+
// Polygons only
86+
return layer->wkbType() == QgsWkbTypes::Type::Polygon || layer->wkbType() == QgsWkbTypes::Type::MultiPolygon;
87+
}
88+
8089
bool QgsMultiRingConstantBufferAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
8190
{
8291
mRingsNumber = parameterAsInt( parameters, QStringLiteral( "RINGS" ), context );

‎src/analysis/processing/qgsalgorithmmultiringconstantbuffer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class QgsMultiRingConstantBufferAlgorithm : public QgsProcessingFeatureBasedAlgo
4242
QString shortHelpString() const override;
4343
QgsMultiRingConstantBufferAlgorithm *createInstance() const override SIP_FACTORY;
4444
void initParameters( const QVariantMap &configuration = QVariantMap() ) override;
45+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
4546

4647
protected:
4748

@@ -60,6 +61,7 @@ class QgsMultiRingConstantBufferAlgorithm : public QgsProcessingFeatureBasedAlgo
6061
double mDistance = 0.0;
6162
bool mDynamicDistance = false;
6263
QgsProperty mDistanceProperty;
64+
6365
};
6466

6567
///@endcond PRIVATE

‎src/analysis/processing/qgsalgorithmorientedminimumboundingbox.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
***************************************************************************/
1717

1818
#include "qgsalgorithmorientedminimumboundingbox.h"
19+
#include "qgsvectorlayer.h"
1920

2021
///@cond PRIVATE
2122

@@ -66,6 +67,14 @@ QgsOrientedMinimumBoundingBoxAlgorithm *QgsOrientedMinimumBoundingBoxAlgorithm::
6667
return new QgsOrientedMinimumBoundingBoxAlgorithm();
6768
}
6869

70+
bool QgsOrientedMinimumBoundingBoxAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
71+
{
72+
if ( ! QgsProcessingFeatureBasedAlgorithm::supportInPlaceEdit( layer ) )
73+
return false;
74+
// Polygons only
75+
return layer->wkbType() == QgsWkbTypes::Type::Polygon || layer->wkbType() == QgsWkbTypes::Type::MultiPolygon;
76+
}
77+
6978
QgsFields QgsOrientedMinimumBoundingBoxAlgorithm::outputFields( const QgsFields &inputFields ) const
7079
{
7180
QgsFields fields = inputFields;

‎src/analysis/processing/qgsalgorithmorientedminimumboundingbox.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class QgsOrientedMinimumBoundingBoxAlgorithm : public QgsProcessingFeatureBasedA
4141
QString groupId() const override;
4242
QString shortHelpString() const override;
4343
QgsOrientedMinimumBoundingBoxAlgorithm *createInstance() const override SIP_FACTORY;
44+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
4445

4546
protected:
4647
QString outputName() const override;

‎src/analysis/processing/qgsalgorithmparallellines.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ QgsFeatureList QgsCreateParallelLinesAlgorithm::processFeature( const QgsFeature
151151
for ( int i = 0; i < count; ++i )
152152
{
153153
offset += offsetStep;
154+
// FIXME: shouldn't we use QgsVectorLayerUtils::createFeature?
154155
QgsFeature offsetFeature = feature;
155156
const QgsGeometry offsetGeometry = geometry.offsetCurve( offset, mSegments, mJoinStyle, mMiterLimit );
156157
offsetFeature.setGeometry( offsetGeometry );

‎src/analysis/processing/qgsalgorithmpromotetomultipart.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ QgsPromoteToMultipartAlgorithm *QgsPromoteToMultipartAlgorithm::createInstance()
6868

6969
bool QgsPromoteToMultipartAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
7070
{
71-
Q_UNUSED( layer );
72-
return false;
71+
if ( ! QgsProcessingFeatureBasedAlgorithm::supportInPlaceEdit( layer ) )
72+
return false;
73+
return QgsWkbTypes::isMultiType( layer->wkbType() );
7374
}
7475

7576
QgsWkbTypes::Type QgsPromoteToMultipartAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const

‎src/analysis/processing/qgsalgorithmsegmentize.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ void QgsSegmentizeByMaximumDistanceAlgorithm::initParameters( const QVariantMap
7878
addParameter( tolerance.release() );
7979
}
8080

81+
bool QgsSegmentizeByMaximumDistanceAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
82+
{
83+
Q_UNUSED( layer );
84+
return false;
85+
}
86+
8187
bool QgsSegmentizeByMaximumDistanceAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
8288
{
8389
mTolerance = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
@@ -167,6 +173,12 @@ void QgsSegmentizeByMaximumAngleAlgorithm::initParameters( const QVariantMap & )
167173
addParameter( tolerance.release() );
168174
}
169175

176+
bool QgsSegmentizeByMaximumAngleAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
177+
{
178+
Q_UNUSED( layer );
179+
return false;
180+
}
181+
170182
bool QgsSegmentizeByMaximumAngleAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
171183
{
172184
mTolerance = parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context );

‎src/analysis/processing/qgsalgorithmsegmentize.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class QgsSegmentizeByMaximumDistanceAlgorithm : public QgsProcessingFeatureBased
4444
QgsSegmentizeByMaximumDistanceAlgorithm *createInstance() const override SIP_FACTORY;
4545
QList<int> inputLayerTypes() const override;
4646
void initParameters( const QVariantMap &configuration = QVariantMap() ) override;
47+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
4748

4849
protected:
4950
QString outputName() const override;
@@ -77,6 +78,7 @@ class QgsSegmentizeByMaximumAngleAlgorithm : public QgsProcessingFeatureBasedAlg
7778
QgsSegmentizeByMaximumAngleAlgorithm *createInstance() const override SIP_FACTORY;
7879
QList<int> inputLayerTypes() const override;
7980
void initParameters( const QVariantMap &configuration = QVariantMap() ) override;
81+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
8082

8183
protected:
8284
QString outputName() const override;

‎tests/src/python/test_qgsprocessinginplace.py

Lines changed: 140 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,33 @@ def reportError(self, error, fatalError=False):
3030
print(error)
3131

3232

33+
base_types = ['Point', 'LineString', 'Polygon']
34+
35+
36+
def _add_multi(base):
37+
return base + ['Multi' + _b for _b in base]
38+
39+
40+
def _add_z(base):
41+
return base + [_b + 'Z' for _b in base]
42+
43+
44+
def _add_m(base):
45+
return base + [_b + 'M' for _b in base]
46+
47+
48+
def _all_true():
49+
types = base_types
50+
types = _add_multi(types)
51+
types = _add_z(types)
52+
types = _add_m(types)
53+
return {t: True for t in types}
54+
55+
56+
def _all_false():
57+
return {t: False for t in _all_true().keys()}
58+
59+
3360
class TestQgsProcessingInPlace(unittest.TestCase):
3461

3562
@classmethod
@@ -75,14 +102,68 @@ def setUpClass(cls):
75102

76103
QgsProject.instance().addMapLayers([cls.vl, cls.multipoly_vl])
77104

78-
def _make_compatible_tester(self, feature_wkt, layer_wkb_name, attrs=[1]):
105+
def _make_layer(self, layer_wkb_name):
79106
fields = QgsFields()
80107
wkb_type = getattr(QgsWkbTypes, layer_wkb_name)
81108
fields.append(QgsField('int_f', QVariant.Int))
82109
layer = QgsMemoryProviderUtils.createMemoryLayer(
83110
'%s_layer' % layer_wkb_name, fields, wkb_type, QgsCoordinateReferenceSystem(4326))
84111
self.assertTrue(layer.isValid())
85112
self.assertEqual(layer.wkbType(), wkb_type)
113+
return layer
114+
115+
def _support_inplace_edit_tester(self, alg_name, expected):
116+
117+
alg = self.registry.createAlgorithmById(alg_name)
118+
for layer_wkb_name, supported in expected.items():
119+
layer = self._make_layer(layer_wkb_name)
120+
print("Checking %s ( %s ) : %s" % (alg_name, layer_wkb_name, supported))
121+
self.assertEqual(alg.supportInPlaceEdit(layer), supported, "Expected: %s - %s = supported: %s" % (alg_name, layer_wkb_name, supported))
122+
123+
def test_support_in_place_edit(self):
124+
125+
ALL = _all_true()
126+
NONE = _all_false()
127+
LINESTRING_ONLY = {t: t.find('LineString') >= 0 for t in _all_true().keys()}
128+
Z_ONLY = {t: t.find('Z') > 0 for t in _all_true().keys()}
129+
M_ONLY = {t: t.rfind('M') > 0 for t in _all_true().keys()}
130+
NOT_M = {t: t.rfind('M') < 1 for t in _all_true().keys()}
131+
POLYGON_ONLY = {t: t in ('Polygon', 'MultiPolygon') for t in _all_true().keys()}
132+
MULTI_ONLY = {t: t.find('Multi') == 0 for t in _all_true().keys()}
133+
SINGLE_ONLY = {t: t.find('Multi') == -1 for t in _all_true().keys()}
134+
LINESTRING_AND_POLYGON_ONLY = {t: (t.find('LineString') >= 0 or t.find('Polygon') >= 0) for t in _all_true().keys()}
135+
LINESTRING_AND_POLYGON_ONLY_NOT_M = {t: (t.rfind('M') < 1 and (t.find('LineString') >= 0 or t.find('Polygon') >= 0)) for t in _all_true().keys()}
136+
LINESTRING_AND_POLYGON_ONLY_NOT_M_NOT_Z = {t: (t.rfind('M') < 1 and t.find('Z') == -1 and (t.find('LineString') >= 0 or t.find('Polygon') >= 0)) for t in _all_true().keys()}
137+
138+
self._support_inplace_edit_tester('native:smoothgeometry', LINESTRING_AND_POLYGON_ONLY)
139+
self._support_inplace_edit_tester('native:parallellines', LINESTRING_ONLY)
140+
self._support_inplace_edit_tester('native:arrayfeatures', ALL)
141+
self._support_inplace_edit_tester('native:reprojectlayer', ALL)
142+
self._support_inplace_edit_tester('qgis:densifygeometries', LINESTRING_AND_POLYGON_ONLY)
143+
self._support_inplace_edit_tester('qgis:densifygeometriesgivenaninterval', LINESTRING_AND_POLYGON_ONLY)
144+
self._support_inplace_edit_tester('native:setzfromraster', Z_ONLY)
145+
self._support_inplace_edit_tester('native:explodelines', LINESTRING_ONLY)
146+
self._support_inplace_edit_tester('native:extendlines', LINESTRING_ONLY)
147+
self._support_inplace_edit_tester('native:fixgeometries', NOT_M)
148+
self._support_inplace_edit_tester('native:minimumenclosingcircle', POLYGON_ONLY)
149+
self._support_inplace_edit_tester('native:multiringconstantbuffer', POLYGON_ONLY)
150+
self._support_inplace_edit_tester('native:orientedminimumboundingbox', POLYGON_ONLY)
151+
self._support_inplace_edit_tester('qgis:orthogonalize', LINESTRING_AND_POLYGON_ONLY)
152+
self._support_inplace_edit_tester('native:removeduplicatevertices', ALL)
153+
self._support_inplace_edit_tester('native:rotatefeatures', ALL)
154+
self._support_inplace_edit_tester('native:segmentizebymaxangle', NONE)
155+
self._support_inplace_edit_tester('native:segmentizebymaxdistance', NONE)
156+
self._support_inplace_edit_tester('native:setmfromraster', M_ONLY)
157+
self._support_inplace_edit_tester('native:simplifygeometries', LINESTRING_AND_POLYGON_ONLY)
158+
self._support_inplace_edit_tester('native:snappointstogrid', ALL)
159+
self._support_inplace_edit_tester('native:multiparttosingleparts', MULTI_ONLY)
160+
self._support_inplace_edit_tester('native:promotetomulti', MULTI_ONLY)
161+
self._support_inplace_edit_tester('native:subdivide', ALL)
162+
self._support_inplace_edit_tester('native:translategeometry', ALL)
163+
self._support_inplace_edit_tester('native:swapxy', ALL)
164+
165+
def _make_compatible_tester(self, feature_wkt, layer_wkb_name, attrs=[1]):
166+
layer = self._make_layer(layer_wkb_name)
86167
layer.startEditing()
87168

88169
f = QgsFeature(layer.fields())
@@ -97,7 +178,7 @@ def _make_compatible_tester(self, feature_wkt, layer_wkb_name, attrs=[1]):
97178
new_features = make_features_compatible([f], layer, context)
98179

99180
for new_f in new_features:
100-
self.assertEqual(new_f.geometry().wkbType(), wkb_type)
181+
self.assertEqual(new_f.geometry().wkbType(), layer.wkbType())
101182

102183
self.assertTrue(layer.addFeatures(new_features), "Fail: %s - %s - %s" % (feature_wkt, attrs, layer_wkb_name))
103184
return layer, new_features
@@ -374,6 +455,63 @@ def test_clip(self):
374455
self.assertEqual(new_features[0].geometry().asWkt(), 'LineString (0 0, 1 1)')
375456
self.assertEqual(new_features[0].attributes(), [1])
376457

458+
def test_fix_geometries(self):
459+
460+
polygon_layer = self._make_layer('Polygon')
461+
self.assertTrue(polygon_layer.startEditing())
462+
f = QgsFeature(polygon_layer.fields())
463+
f.setAttributes([1])
464+
# Flake!
465+
f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0, 2 2, 0 2, 2 0, 0 0))'))
466+
self.assertTrue(f.isValid())
467+
f2 = QgsFeature(polygon_layer.fields())
468+
f2.setAttributes([1])
469+
f2.setGeometry(QgsGeometry.fromWkt('POLYGON((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))'))
470+
self.assertTrue(f2.isValid())
471+
self.assertTrue(polygon_layer.addFeatures([f, f2]))
472+
polygon_layer.commitChanges()
473+
polygon_layer.rollBack()
474+
self.assertEqual(polygon_layer.featureCount(), 2)
475+
476+
QgsProject.instance().addMapLayers([polygon_layer])
477+
478+
old_features, new_features = self._alg_tester(
479+
'native:fixgeometries',
480+
polygon_layer,
481+
{
482+
}
483+
)
484+
self.assertEqual(polygon_layer.featureCount(), 3)
485+
wkt1, wkt2, _ = [f.geometry().asWkt() for f in new_features]
486+
self.assertEqual(wkt1, 'Polygon ((0 0, 1 1, 2 0, 0 0))')
487+
self.assertEqual(wkt2, 'Polygon ((1 1, 0 2, 2 2, 1 1))')
488+
489+
# Test with Z (interpolated)
490+
polygonz_layer = self._make_layer('PolygonZ')
491+
self.assertTrue(polygonz_layer.startEditing())
492+
493+
f3 = QgsFeature(polygonz_layer.fields())
494+
f3.setAttributes([1])
495+
f3.setGeometry(QgsGeometry.fromWkt('POLYGON Z((0 0 1, 2 2 1, 0 2 3, 2 0 4, 0 0 1))'))
496+
self.assertTrue(f3.isValid())
497+
self.assertTrue(polygonz_layer.addFeatures([f3]))
498+
polygonz_layer.commitChanges()
499+
polygonz_layer.rollBack()
500+
self.assertEqual(polygonz_layer.featureCount(), 1)
501+
502+
QgsProject.instance().addMapLayers([polygonz_layer])
503+
504+
old_features, new_features = self._alg_tester(
505+
'native:fixgeometries',
506+
polygonz_layer,
507+
{
508+
}
509+
)
510+
self.assertEqual(polygonz_layer.featureCount(), 2)
511+
wkt1, wkt2 = [f.geometry().asWkt() for f in new_features]
512+
self.assertEqual(wkt1, 'PolygonZ ((0 0 1, 1 1 2.25, 2 0 4, 0 0 1))')
513+
self.assertEqual(wkt2, 'PolygonZ ((1 1 2.25, 0 2 3, 2 2 1, 1 1 2.25))')
514+
377515

378516
if __name__ == '__main__':
379517
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.