lunes, 22 de octubre de 2018

Selección avanzada de rasgos con objetos de QgsExpression en PyQGIS 3

En el post anterior se trató el tópico relativo a la selección avanzada de rasgos con objetos de QgsFeatureRequest en PyQGIS 3. Allí, para la selección de rasgos, al método 'getFeatures' se le pasaba como argumento un objeto de la clase QgsFeatureRequest con base en un string (unicode) con sintaxis tipo SQL y relacionada a los campos de atributos. Otra forma de realizar la selección de rasgos es transformar el string en un objeto de la clase QgsExpression y verificar para cada rasgo, mediante su método 'evaluate', si se cumple la condición tipo SQL. Esto produce un código más conciso; tal como se presenta a continuación:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
project = QgsProject.instance()

layer = project.mapLayersByName('polygon8')

expression = QgsExpression( '"area" > 2e8 AND "area" < 3e8' )

context = QgsExpressionContext()
scope = QgsExpressionContextScope()
context.appendScope(scope)

eval = [ [scope.setFeature(feat), expression.evaluate(context)][1] 
         for feat in layer[0].getFeatures() ]

feats = [ item[1] for item in enumerate(layer[0].getFeatures()) 
          if eval[item[0]] == 1]

idx = [ i for i in range(len(eval)) if eval[i] == 1 ] 

for i,feat in enumerate(feats): 
    area = feat.geometry().area() 
    print ("area{:.0f} = {:.2f}".format(idx[i], area))

A diferencia de PyQGIS 2 aquí es necesario emplear dos clases adicionales, QgsExpressionContext y QgsExpressionContextScope, para que la evaluación se efectúe de la manera esperada. La lista ‘eval’ contiene una combinación de unos y ceros. Los unos corresponden a aquellos rasgos donde se cumple la expresión a evaluar. Por tanto, los rasgos (feats) se seleccionan posteriormente con base en esta lista ‘eval’.

Se va a aplicar al mismo vectorial (polygon8.shp) del post anterior pero la consulta es un poco más compleja y basada en el campo de atributo ‘area’. Cuando se ejecuta el código en la Python Console de QGIS, tal como evidencia la imagen siguiente, los resultados son los esperados.


Por otra parte, al igual que en el caso anterior, se incluye el código complementario para crear la memory layer basada en esa consulta SQL.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
.
.
.

epsg = layer[0].crs().postgisSrid()

uri = "Polygon?crs=epsg:" + str(epsg) + "&field=id:integer""&index=yes"

mem_layer = QgsVectorLayer(uri, 
                           'mem_layer', 
                           'memory')

prov = mem_layer.dataProvider()

for i, feat in enumerate(feats):
    feat.setAttributes([i])
    mem_layer.addFeature(feat)

prov.addFeatures(feats)

QgsProject.instance().addMapLayer(mem_layer)

El resultado de la ejecución de la porción de código anterior puede ser observado en la imagen siguiente:

No hay comentarios: