@@ -17,6 +17,8 @@
QgsFeature , QgsGeometry , QgsSettings , QgsApplication , QgsMemoryProviderUtils , QgsWkbTypes , QgsField , QgsFields , QgsProcessingFeatureSourceDefinition , QgsProcessingContext , QgsProcessingFeedback , QgsCoordinateReferenceSystem , QgsProject , QgsProcessingException
)
from processing .core .Processing import Processing
from processing .core .ProcessingConfig import ProcessingConfig
from processing .tools import dataobjects
from processing .gui .AlgorithmExecutor import execute_in_place_run
from qgis .testing import start_app , unittest
from qgis .PyQt .QtTest import QSignalSpy
@@ -132,7 +134,8 @@ def test_support_in_place_edit(self):
Z_ONLY = {t : t .find ('Z' ) > 0 for t in _all_true ().keys ()}
M_ONLY = {t : t .rfind ('M' ) > 0 for t in _all_true ().keys ()}
NOT_M = {t : t .rfind ('M' ) < 1 and t != 'NoGeometry' for t in _all_true ().keys ()}
POLYGON_ONLY = {t : t in ('Polygon' , 'MultiPolygon' ) for t in _all_true ().keys ()}
POLYGON_ONLY = {t : t .find ('Polygon' ) for t in _all_true ().keys ()}
POLYGON_ONLY_NOT_M_NOT_Z = {t : t in ('Polygon' , 'MultiPolygon' ) for t in _all_true ().keys ()}
MULTI_ONLY = {t : t .find ('Multi' ) == 0 for t in _all_true ().keys ()}
SINGLE_ONLY = {t : t .find ('Multi' ) == - 1 for t in _all_true ().keys ()}
LINESTRING_AND_POLYGON_ONLY = {t : (t .find ('LineString' ) >= 0 or t .find ('Polygon' ) >= 0 ) for t in _all_true ().keys ()}
@@ -149,9 +152,9 @@ def test_support_in_place_edit(self):
self ._support_inplace_edit_tester ('native:explodelines' , LINESTRING_ONLY )
self ._support_inplace_edit_tester ('native:extendlines' , LINESTRING_ONLY )
self ._support_inplace_edit_tester ('native:fixgeometries' , NOT_M )
self ._support_inplace_edit_tester ('native:minimumenclosingcircle' , POLYGON_ONLY )
self ._support_inplace_edit_tester ('native:multiringconstantbuffer' , POLYGON_ONLY )
self ._support_inplace_edit_tester ('native:orientedminimumboundingbox' , POLYGON_ONLY )
self ._support_inplace_edit_tester ('native:minimumenclosingcircle' , POLYGON_ONLY_NOT_M_NOT_Z )
self ._support_inplace_edit_tester ('native:multiringconstantbuffer' , POLYGON_ONLY_NOT_M_NOT_Z )
self ._support_inplace_edit_tester ('native:orientedminimumboundingbox' , POLYGON_ONLY_NOT_M_NOT_Z )
self ._support_inplace_edit_tester ('qgis:orthogonalize' , LINESTRING_AND_POLYGON_ONLY )
self ._support_inplace_edit_tester ('native:removeduplicatevertices' , GEOMETRY_ONLY )
self ._support_inplace_edit_tester ('native:rotatefeatures' , GEOMETRY_ONLY )
@@ -172,6 +175,7 @@ def test_support_in_place_edit(self):
self ._support_inplace_edit_tester ('native:difference' , GEOMETRY_ONLY )
self ._support_inplace_edit_tester ('native:dropgeometries' , ALL )
self ._support_inplace_edit_tester ('native:splitwithlines' , LINESTRING_AND_POLYGON_ONLY )
self ._support_inplace_edit_tester ('native:buffer' , POLYGON_ONLY_NOT_M_NOT_Z )
def _make_compatible_tester (self , feature_wkt , layer_wkb_name , attrs = [1 ]):
layer = self ._make_layer (layer_wkb_name )
@@ -659,6 +663,71 @@ def test_fix_geometries(self):
self .assertEqual (wkt1 , 'PolygonZ ((0 0 1, 1 1 2.25, 2 0 4, 0 0 1))' )
self .assertEqual (wkt2 , 'PolygonZ ((1 1 2.25, 0 2 3, 2 2 1, 1 1 2.25))' )
def _test_difference_on_invalid_geometries (self , geom_option ):
polygon_layer = self ._make_layer ('Polygon' )
self .assertTrue (polygon_layer .startEditing ())
f = QgsFeature (polygon_layer .fields ())
f .setAttributes ([1 ])
# Flake!
f .setGeometry (QgsGeometry .fromWkt ('Polygon ((0 0, 2 2, 0 2, 2 0, 0 0))' ))
self .assertTrue (f .isValid ())
self .assertTrue (polygon_layer .addFeatures ([f ]))
polygon_layer .commitChanges ()
polygon_layer .rollBack ()
self .assertEqual (polygon_layer .featureCount (), 1 )
overlay_layer = self ._make_layer ('Polygon' )
self .assertTrue (overlay_layer .startEditing ())
f = QgsFeature (overlay_layer .fields ())
f .setAttributes ([1 ])
f .setGeometry (QgsGeometry .fromWkt ('Polygon ((0 0, 2 0, 2 2, 0 2, 0 0))' ))
self .assertTrue (f .isValid ())
self .assertTrue (overlay_layer .addFeatures ([f ]))
overlay_layer .commitChanges ()
overlay_layer .rollBack ()
self .assertEqual (overlay_layer .featureCount (), 1 )
QgsProject .instance ().addMapLayers ([polygon_layer , overlay_layer ])
old_features = [f for f in polygon_layer .getFeatures ()]
# 'Ignore features with invalid geometries' = 1
ProcessingConfig .setSettingValue (ProcessingConfig .FILTER_INVALID_GEOMETRIES , geom_option )
feedback = ConsoleFeedBack ()
context = dataobjects .createContext (feedback )
context .setProject (QgsProject .instance ())
alg = self .registry .createAlgorithmById ('native:difference' )
self .assertIsNotNone (alg )
parameters = {
'OVERLAY' : overlay_layer ,
'INPUT' : polygon_layer ,
'OUTPUT' : ':memory' ,
}
old_features = [f for f in polygon_layer .getFeatures ()]
self .assertTrue (polygon_layer .startEditing ())
polygon_layer .selectAll ()
ok , _ = execute_in_place_run (
alg , parameters , context = context , feedback = feedback , raise_exceptions = True )
new_features = [f for f in polygon_layer .getFeatures ()]
return old_features , new_features
def test_difference_on_invalid_geometries (self ):
"""Test #20147 difference deletes invalid geometries"""
old_features , new_features = self ._test_difference_on_invalid_geometries (1 )
self .assertEqual (len (new_features ), 1 )
old_features , new_features = self ._test_difference_on_invalid_geometries (0 )
self .assertEqual (len (new_features ), 1 )
old_features , new_features = self ._test_difference_on_invalid_geometries (2 )
self .assertEqual (len (new_features ), 1 )
if __name__ == '__main__' :
unittest .main ()
@elpaso , this change has regressed in place editing for convex hull alg (possibly more, just spotted that one). When selecting a few features, enabling in-place editing and selecting the convex hull alg, the following error is thrown: