Como escrever um algoritmo de processamento (Processing) usando Python
Luís Calisto

É possível criar scripts para integrar a “Processing Toolbox” de forma simples, rápida e desta forma aliar a interface gráfica do QGIS com a versatilidade de um script Python.

Recentemente necessitei de calcular a área ocupada pelos pixeis de um raster cujos valores se encontram dentro de um determinado intervalo. Como não encontrei uma solução rápida que me agradasse, e talvez um pouco por preguiça, optei por fazer um pequeno algoritmo em Python para o Processing que demorou apenas uns minutos e que vamos aqui exemplificar.

Existem duas formas de escrever um script para o “Processing“:

Para criar um script basta selecionar a opção “Create new script” no menu Python

Esta opção irá abrir o editor de scripts do “Processing” onde iremos escrever o nosso script Python.

@alg

O decorador @alg vai-nos permitir de forma simples comunicar com a “Processing Toolbox” e assim definir por exemplo o nome e localização do nosso algoritmo, inputs e outputs. Para mais informações podemos consultar o seguinte link https://docs.qgis.org/3.10/en/docs/user_manual/processing/scripts.html?highlight=python#scripts-alg

O algoritmo

O nosso script tem o objectivo de calcular a área de um raster que se encontra numa determinada gama de valores. Para isso vamos executar os seguintes passos:

  • Definir os inputs, outputs, nome e localização do algoritmo
  • Abrir o raster
  • Extrair alguns parâmetros utilizando o pyQGIS. Em alternativa tambem poderiamos usar o GDAL.
  • Abrir o raster com o GDAL, desta forma exemplificamos como se converte um raster de pyQGIS para GDAL e assim usufruir as muitas capacidades deste último
  • Converter o raster para um array numpy
  • Calcular o número de pixeis que se encontram no intervalo de valores com numpy
  • Calcular a área
  • Retornar os valores previamente calculados.

O script

from qgis import processing
from qgis.processing import alg
from qgis.core import QgsProject
from osgeo import gdal, gdal_array

@alg(name='countpixels', label='Count pixels from a raster for a given range',
     group='myscripts', group_label='My scripts')

@alg.input(type=alg.RASTER_LAYER, name='INPUT', label='Input raster layer')

@alg.input(type=alg.BAND, name='BAND',defaultValue=1, parentLayerParameterName='INPUT', label='Raster band')
           
@alg.input(type=alg.NUMBER, name='MINIMUM', label='Min Value',
           default=0)
@alg.input(type=alg.NUMBER, name='MAXIMUM', label='Max Value',
           default=0.9)

@alg.output(type=alg.NUMBER, name='NUMBEROFPIXELS',
            label='Number of pixels')
@alg.output(type=alg.NUMBER, name='XSIZE',
            label='X pixel size')
@alg.output(type=alg.NUMBER, name='YSIZE',
            label='Y pixel size')
@alg.output(type=alg.NUMBER, name='AREA',
            label='Total Area')
            
def countpixels(instance, parameters, context, feedback, inputs):
    """
    Description of the algorithm.
    (If there is no comment here, you will get an error)
    """
    #parameters
    input_rasterLayer = instance.parameterAsRasterLayer(parameters,
                                                 'INPUT', context)
    input_rasterBand = instance.parameterAsInt(parameters,
                                                 'BAND', context)
    pixelSizeX= input_rasterLayer.rasterUnitsPerPixelX()
    pixelSizeY= input_rasterLayer.rasterUnitsPerPixelY()
    
    ds=gdal.Open(input_rasterLayer.source())
    
    band1Array=ds.GetRasterBand(input_rasterBand).ReadAsArray()
    size=band1Array[(parameters['MINIMUM'] < band1Array) & (band1Array < parameters['MAXIMUM'])].size   
    # Calculate are in square km
    area= pixelSizeX*pixelSizeY*size*0.000001 
    if feedback.isCanceled():
        return {}
    return {'NUMBEROFPIXELS': size, 'XSIZE': pixelSizeX,'YSIZE': pixelSizeY, 'AREA': area}

Como já descrito o decorador @alg é muito util, com ele definimos em primeiro lugar, o nome do nosso script, o titulo e o grupo em que este vai aparecer na “Processing Toolbox“.

Nas linhas seguintes vamos definir os vários inputs nomeadamente:

  • O raster;
  • A banda do raster, importante realçar a importância da tag parentLayerParameterName de forma a podermos definir qual o raster à qual a banda se refere;
  • Os valores de mínimo e máximo.

Depois dos inputs definimos os outputs que no nosso caso são apenas valores numéricos. Porém poderiam ser camadas raster ou vectoriais.

Por fim criamos a função principal com o mesmo nome que foi definido anteriormente no primeiro @alg com o nome do algoritmo. De realçar que a docstring na função principal é obrigatória e vai posteriormente aparecer no menu de ajuda. Acrescento ainda que no nosso caso, apenas temos de retornar um dicionário com os valores e associá-los às variáveis predefinidas no output.

Ao terminar de escrever o script no editor de scripts podemos guardar e ao clicar em “Run Script“, caso não haja nenhum erro de syntax, este irá abrir a janela com o nosso script.

A nossa janela irá aparecer automaticamente sem ser necessário qualquer código para controlar a GUI.

A partir deste momento o script deverá aparecer na “Processing Toolbox” dentro do grupo que definimos anteriormente no @alg

Ao correr o script iremos obter no log os resultados pretendidos. Este log é importante também para efeitos de “debug” pois para além dos resultados vão ser apresentados outros valores tais como os inputs, entre outros.

Importa realçar algumas regras de boas práticas:

  • Não carregar as camadas resultantes no script. Deve-se deixar o “Processing” carregar as camadas definidas no output;
  • Devemos sempre declarar as variáveis de output;
  • Não se devem carregar elementos do GUI ou “message boxes” com o script. Deve-se em alternativa utilizar o objecto de feedback QgsProcessingFeedback ou então QgsProcessingException

Como podemos ver através deste post, é muito fácil criar um algoritmo no “Processing” com apenas alguns conhecimentos de Python. Com o decorador @alg torna-se bastante simples deixar o “Processing” criar a GUI e assim focarmos-nos no que mais interessa, o nosso código.

Pessoalmente sou um fã do GDAL/OGR e numpy em geoprocessamento, pelo que sempre que posso utilizo-os nos meus scripts, estas bibliotecas já vêem no QGIS e podemos utilizá-las facilmente no “Processing“.

2 comentários em “Como escrever um algoritmo de processamento (Processing) usando Python
Luís Calisto

  1. Avatar
    Antonio Sobral Almeida Responder

    Olá,
    Os algoritmos de processamento (processing algorithms) são a melhor forma de automatizar rotinas e cálculos, evitando-se toda aquela confusão (ou trapalhada) da criação de plugins.
    Pena é que os responsáveis pela gestão do repositório de plugins não deixem, ou não abram, um lugar (site) específico para os algoritmos de processamento, que têm normalmente a forma de um script (*.py).
    Mas nós, utilizadores portugueses, não podemos criar esse site para repositório, troca de ideias e desenvolvimento de scripts?
    Bom trabalho a todos!

    • Luís Calisto
      Luís Calisto Autor do artigoResponder

      Olá,

      Desde já obrigado pelo seu comentário,

      Os algoritmos de processamento e os plugins devem ser vistos de forma distinta. Por um lado os algoritmos são bastante mais simples e acabam por em muitos casos solucionar as necessidades mais comuns. Por outro lado os plugins, devido à sua maior complexidade, permitem uma maior customização e portanto servem para solucionar problemas maiores onde os algoritmos de processamento não são suficientes ou onde é necessário um maior controlo da GUI.
      Relativamente a um repositório de partilha, creio que o recurso que sugere já existe com o plugin “QGIS Resource Sharing” https://plugins.qgis.org/plugins/qgis_resource_sharing

      Cumprimentos

Responder a Luís Calisto Cancelar resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *