Escrevendo um script de processamento python com o Qgis 3.0

14

Após a atualização para o Qgis 3.0, tornou-se muito difícil encontrar informações sobre a gravação de scripts de processamento no Qgis 3.0.

@Underdark (veja aqui ) forneceu uma base para o esqueleto. Este código também parece ter sido adicionado no Qgis, ao escrever um novo script a partir do modelo (Qgis 3.0.2).

No entanto, não consegui encontrar nenhuma maneira de ajudar iniciantes em python como eu a entender como alterar esse código, especialmente para as camadas de entrada e saída.

Meu objetivo é escrever um script com duas camadas raster e um duplo como entrada, produzindo duas camadas.

Quais seriam as alterações necessárias no código de exemplo para permitir isso?

Para o Qgis 2.x, eu teria usado a seguinte sintaxe:

##Layer1=raster
##Layer2=raster 
##myDouble=Double
##OutLayer1=output raster
##OutLayer2=output raster   

Pelo que entendi, as alterações devem ser feitas no procedimento a seguir, mas não tenho certeza do que colocar em prática.

    def initAlgorithm(self, config=None):
    self.addParameter(QgsProcessingParameterFeatureSource(
        self.INPUT,
        self.tr("Input layer"),
        [QgsProcessing.TypeVectorAnyGeometry]))
    self.addParameter(QgsProcessingParameterFeatureSink(
        self.OUTPUT,
        self.tr("Output layer"),
        QgsProcessing.TypeVectorAnyGeometry))

Em 16 de maio, a documentação da API pyg do Qgis foi lançada. No entanto, ainda não está claro para mim como usá-lo aqui. (O que pode muito bem ser uma falta de conhecimento de python)

Kantan
fonte
1
Você poderia fornecer uma amostra do código usado para o mesmo objetivo no qgis 2.xx A documentação no qgis 3.x estará disponível aqui: docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/… assim que for Atualizada. Os problemas do documento são rastreados aqui: github.com/qgis/QGIS-Documentation/issues
Nono
Resposta editada com o exemplo de código. Obrigado pelos links, eu já estava seguindo o livro de receitas, mas infelizmente não consegui encontrar minha resposta lá!
Kantan
Eu li sobre os documentos da API do Qgis, mas não consigo me relacionar com isso e o código do @Underdark. (veja a edição para os links)
Kantan

Respostas:

26

Com a transição do QGIS2.x para o QGIS3.x, toda a estrutura de processamento foi reformulada e grande parte dela é executada agora como classes C ++ com as quais você pode interagir usando o Python. Infelizmente, a sintaxe simples dos parâmetros para IO de dados / conjunto de dados não é mais válida. A nova estrutura de parâmetros é muito mais orientada após os algoritmos de processamento interno (Python-) que você encontra pré-instalados na caixa de ferramentas.

A meu ver, você já seguiu a descrição da nova estrutura de algoritmos do @underdark. Mas, para ajustar essa estrutura aos seus requisitos (camadas raster, entrada dupla, etc.), é necessário alterar o código em vários locais do script. Eu codifiquei um exemplo aproximado com uma breve explicação para você (apenas um esqueleto de algoritmo baseado no exemplo @underdarks):

from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing, QgsProcessingAlgorithm, 
QgsProcessingParameterRasterLayer,QgsProcessingParameterNumber, 
QgsProcessingParameterRasterDestination)

class RasterAlg(QgsProcessingAlgorithm):
    INPUT_RASTER_A = 'INPUT_RASTER_A'
    INPUT_RASTER_B = 'INPUT_RASTER_B'
    INPUT_DOUBLE = 'INPUT_DOUBLE'
    OUTPUT_RASTER_A = 'OUTPUT_RASTER_A'
    OUTPUT_RASTER_B = 'OUTPUT_RASTER_B'

    def __init__(self):
        super().__init__()

    def name(self):
        return "RasterAlg"

    def tr(self, text):
        return QCoreApplication.translate("RasterAlg", text)

    def displayName(self):
        return self.tr("RasterAlg script")

    def group(self):
        return self.tr("RasterAlgs")

    def groupId(self):
        return "RasterAlgs"

    def shortHelpString(self):
        return self.tr("RasterAlg script without logic")

    def helpUrl(self):
        return "https://qgis.org"

    def createInstance(self):
        return type(self)()

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterRasterLayer(
            self.INPUT_RASTER_A,
            self.tr("Input Raster A"), None, False))
        self.addParameter(QgsProcessingParameterRasterLayer(
            self.INPUT_RASTER_B,
            self.tr("Input Raster B"), None, False))
        self.addParameter(QgsProcessingParameterNumber(
            self.INPUT_DOUBLE, 
            self.tr("Input Double"), 
            QgsProcessingParameterNumber.Double,
            QVariant(1.0)))
        self.addParameter(QgsProcessingParameterRasterDestination(
            self.OUTPUT_RASTER_A,
            self.tr("Output Raster A"),
            None, False))
        self.addParameter(QgsProcessingParameterRasterDestination(
            self.OUTPUT_RASTER_B,
            self.tr("Output Raster B"),
            None, False))

    def processAlgorithm(self, parameters, context, feedback):
        raster_a = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER_A, context)
        raster_b = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER_B, context)
        double_val = self.parameterAsDouble(parameters, self.INPUT_DOUBLE,context)
        output_path_raster_a = self.parameterAsOutputLayer(parameters, self.OUTPUT_RASTER_A, context)
        output_path_raster_b = self.parameterAsOutputLayer(parameters, self.OUTPUT_RASTER_B, context)

        #DO SOME CALCULATION

        results = {}
        results[self.OUTPUT_RASTER_A] = output_path_raster_a
        results[self.OUTPUT_RASTER_B] = output_path_raster_b
        return results

Quais etapas foram feitas?

  1. Importe todas as classes necessárias.
  2. Defina o algoritmo como uma classe herdada de QgsProcessingAlgorithm.
  3. Primeiro, você deve declarar os nomes dos parâmetros de entrada e saída como variáveis ​​de string (nomes de parâmetros) da classe de algoritmos (ou seja, INPUT_RASTER_A = 'INPUT_RASTER_A') para fazer referência ao seu algoritmo com os parâmetros fornecidos pela estrutura de processamento.
  4. Adicione os métodos que conectam seu algoritmo à GUI da caixa de ferramentas de processamento e forneça instruções, etc.
  5. Em seguida, você adiciona os parâmetros da estrutura de processamento. Aqueles são definidos como classes filhas de QgsProcessingParameterType- no caso do seu algoritmo: QgsProcessingParameterRasterLayer, QgsProcessingParameterNumbere assim por diante. Você pode consultar as entradas da API (ou seja, QgsProcessingParameterRasterLayer) para passar os argumentos corretos e construir os objetos de parâmetro.
  6. Passe os parâmetros ao lado de contexte feedbackobjetos para o processAlgorithm()método em que você obtém os conjuntos de dados de entrada dos parâmetros em tempo de execução (nesse caso, objetos QgsRasterLayer usando o parameterAsRasterLayer()método etc.).
  7. Faça o seu cálculo.
  8. Adicione as saídas ao dicionário de resultados e retorne-as como resultado da chamada processAlgorithm().

Espero poder fornecer algumas idéias sobre como projetar seus algoritmos python no QGIS3. Sempre que você está parado, é sempre útil observar como os algoritmos existentes da estrutura de processamento lidam com os parâmetros. Você pode dar uma olhada neles aqui .

root676
fonte
1
Boa redação! Se importa se eu o adicionar ao github.com/qgis/QGIS/blob/master/doc/porting_processing.dox ?
Ndawson 28/05
Ficaria honrado se você o adicionar à documentação do qgis. Por favor, faça isso! Existem pré-requisitos para contribuir com mais documentação python para o qgis3? Eu acho que isso é essencial para uma base de usuários mais ampla em termos de scripts e programadores.
root676
1
Sem pré-requisitos. Na verdade, é muito fácil adicionar ao livro de receitas oficial do python por meio de solicitações pull do GitHub (todas as edições podem ser feitas no site do GitHub: github.com/qgis/QGIS-Documentation/tree/master/source/docs/… ). Adicionar mais exemplos aos documentos oficiais também seria muito bem-vindo!
Ndawson 29/05
1
Obrigado pela sua resposta! Eu estava ocupado hoje, mas vou tentar cavar amanhã. Parece realmente promissor.
Kantan
2
Essa é definitivamente uma ótima resposta, obrigado pelos detalhes e referências. O link para os scripts no gitHub é uma verdadeira mina de ouro! No início, a declaração QVariant me deu um erro, mas quando eu a redigitei no editor e usei a conclusão automática, o erro desapareceu. Agora é realmente necessário um grande passo para mergulhar nos scripts, espero que não desanime novos programadores. Quanto mais documentação estiver disponível, espero que esteja ficando mais clara!
Kantan