Search code examples
pythongisqgis

Function "eliminate selected polygons" doesn't work with PyQGIS


I have a vector layer (correction) based on a raster layer. It contains some little features and I want to merge those features to bigger ones. The function "eliminate selected polygons" seems to do the trick when used in QGIS but when I used it in pyQGIS the selected features aren't taken into account.

This is my layer with the selection

This is the expected output, the one from QGIS

This is my actual output, the one from pyQGIS

When I run my code the function doesn't trigger the CRITICAL error. The first log is my code and the second is the process without any polygon selection.

Does anyone already have this issue ? Is it possible to use another similar function ? Thanks in advance

QGIS Version : 3.14 (pi)

OS : Linux Mint 20 Ulyana (Ubuntu focal 20.04)

This is my code :

chemin_sortie = "/projet_qgis/pente/donnees_traitement/"
#Input   
correction = chemin_sortie + 'correction' + '.shp'
correction_layer = iface.addVectorLayer(correction, '', 'ogr')
#Output 
pente_vecteur_grand = chemin_sortie + 'pente_vecteur_grand' + '.shp'

#Selection and process
correction_layer.selectByExpression('$area < 500')  
processing.run("qgis:eliminateselectedpolygons", {'INPUT':correction_layer,'MODE':2,'OUTPUT':pente_vecteur_grand})
pente_vecteur_grand_layer = iface.addVectorLayer(pente_vecteur_grand, '', 'ogr')

Solution

  • I made a function to realize the same operation as qgis:eliminateselectedpolygons. There is still some differences, I think it's related to the order of the polygons to merge. This function is optimizable but works for me.

    This is my layer with the selection

    This is the output with the qgis function with min area

    This is the output of the pyqgis function with min area

    This is the output with the qgis function with max area

    This is the output of the pyqgis function with max area

    This is the code :

    import types
        
    chemin_sortie = "/projet_qgis/pente/donnees_traitement/"
        
    # Input   
        correction = chemin_sortie + 'correction' + '.shp'
        correction_layer = iface.addVectorLayer(correction, '', 'ogr')
        
    # Selection
        correction_layer.selectByExpression('$area < 500')
    
    # Functions
    def get_key(dict, val):
        for key, value in dict.items():
             if val == value:
                 return key
    def eliminateselectedpolygon(layer, param):
        if isinstance(param, types.BuiltinFunctionType):
            features = layer.getFeatures()
            n=0
            index = QgsSpatialIndex()
            allfeatures = {}
            selectedfeatures = {}
    
            for f in features:
                value=[]
                n = n+1
                geom = f.geometry()
                aire = geom.area()
                aire = '%.5f' % aire
                aire = float(aire)
                value.append(f)
                value.append(aire)
                allfeatures[f.id()]=value
                index.addFeature(f)
            for f in layer.selectedFeatures():
                selectedfeatures[f.id()]=f.id()
            suppression = []
    
            while len(selectedfeatures) != 0:
                f_id = min(selectedfeatures.values())
                f = allfeatures[f_id]
                f_geom = f[0]
                ids = index.intersects(f_geom.geometry().boundingBox())
                dict_aire = {}
                for a_id in ids:
                    if a_id != f_id and allfeatures[a_id][0].geometry().touches(f_geom.geometry()) and QgsWkbTypes.displayString(int(allfeatures[a_id][0].geometry().intersection(f_geom.geometry()).wkbType())) != "Point" :
                        dict_aire[a_id]=allfeatures[a_id][1]
                a_id = get_key(dict_aire, param(dict_aire.values()))
                a = allfeatures[a_id]
                a_geom = a[0]
                attrs = layer.getFeature(a_id).attributes()
                suppression.append(f_id)
                suppression.append(a_id)
                if a_id in selectedfeatures:
                    del selectedfeatures[a_id]
                del allfeatures[a_id]
                index.deleteFeature(a_geom)
                layer.startEditing()
                geom = None
                geom = QgsGeometry.fromWkt('GEOMETRYCOLLECTION()')
                geom = geom.combine(f_geom.geometry())
                geom = geom.combine(a_geom.geometry())
                feat = QgsFeature(layer.fields())
                feat.setGeometry(geom)
                feat.setAttributes(attrs)
                layer.addFeature(feat)
                feat.setId(abs(n))
                layer.commitChanges()
                layer.triggerRepaint()
                value_feat = []
                geom = feat.geometry()
                aire = geom.area()
                aire = '%.5f' % aire
                aire = float(aire)
                value_feat.append(feat)
                value_feat.append(aire)
                allfeatures[feat.id()]=value_feat
                index.addFeature(feat)
                del selectedfeatures[f_id]
                del allfeatures[f_id]
                index.deleteFeature(f_geom)
                n = n + 1
            layer.startEditing()
            for feature in suppression:
                res = layer.deleteFeature(feature)
            layer.commitChanges()
            layer.triggerRepaint()
            return layer
        print("Param should be min or max")
        return None
    
    eliminateselectedpolygon(correction_layer, max)