Ejercicio 4: NDVI con imágenes Landsat 8 o 9
Esta herramienta calcula el NDVI (Índice de Vegetación de Diferencia Normalizada) a partir de dos bandas de una imagen Landsat 8 o 9, usando la calculadora ráster de QGIS de forma automática desde código.
NDVI = (Banda 5 - Banda 4) / (Banda 5 + Banda 4)
Donde la Banda 4 es la reflectancia en el rojo (Red) y la Banda 5 es la reflectancia en el infrarrojo cercano (NIR). Este índice permite estimar qué tan densa y saludable está la vegetación en una zona.
Conceptos clave de POO y PyQGIS usados en el ejercicio
-
Docstring de clase: a diferencia de los ejercicios 1 a 3,
aquí la clase
Landsat_NDVIincluye un comentario de documentación (docstring) justo debajo de su declaración, listando los 8 métodos que la componen. Es una buena práctica de POO: documentar el "contrato" que cumple la clase antes de implementarlo. -
Docstrings en cada método: cada método (
name,displayName, etc.) tiene su propio comentario explicando qué debe devolver, siguiendo el formato estándar que usa QGIS en las plantillas oficiales de sus herramientas de procesamiento. -
Método nuevo:
shortHelpString: este método no aparecía en los ejercicios 1 a 3. Sirve para mostrar un texto de ayuda dentro de la propia ventana de la herramienta en QGIS, explicando al usuario qué hace y cómo usarla. -
Cadenas con formato (f-strings):
f'("{parameters["b5"]}@1" - ...)'es una cadena de texto especial (comienza conf) que permite insertar el valor de una variable directamente dentro del texto, usando llaves{ }. -
Ejecutar la calculadora ráster desde código:
processing.run('qgis:rastercalculator', ...)arma una expresión matemática como texto y se la entrega a la herramienta "Calculadora ráster" de QGIS, en vez de tener que escribir manualmente la fórmula dentro de la interfaz gráfica.
Análisis y explicación de la estructura y lógica del código
"""
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
Script para una herramienta de cálculo del NDVI a partir de imágenes Landsat 8 o 9
La fórmula de cálculo es:
NDVI = (Banda 5 - Banda 4) / (Banda 5 + Banda 4)
Por lo tanto, se ocupan los archivos de las bandas que aparecen en la fórmula anterior.
"""
from qgis.core import QgsProcessingAlgorithm #Clase de la que se hereda en nuestra herramienta
from qgis.core import QgsProcessingParameterRasterLayer #Para cargar capas raster al diccionario de parámetros
from qgis.core import QgsProcessingParameterRasterDestination #Para guardar una salida ráster
from qgis.core import QgsProcessingMultiStepFeedback #Para mostrar el progreso de la operación
import processing #Para usar en "processing.run()"
class Landsat_NDVI(QgsProcessingAlgorithm):
"""
Métodos básicos de la herramienta:
1. Nombre de la la herramienta (name).
2. Nombre a mostyrar en la GUI (displayName).
3. Grupo de herramientas al que pertenece la herramienta actual (group)
4. ID del grupo al que pertence la herramienta (groupId)
5. Ayuda corta de la herramienta (shortHelpString)
6. Cargar entradas como parámetros de algorítmo (initAlgorithm)
7. Algoritmo de procesamiento (processAlgorithm)
8. Creación de la instancia (createInstance)
"""
def name(self) -> str:
"""
Returns the algorithm name, used for identifying the algorithm. This
string should be fixed for the algorithm, and must not be localised.
The name should be unique within each provider. Names should contain
lowercase alphanumeric characters only and no spaces or other
formatting characters.
"""
return "landsat_ndvi"
def displayName(self) -> str:
"""
Returns the translated algorithm name, which should be used for any
user-visible display of the algorithm name.
"""
return "NDVI con imágenes Landsat 8 o 9"
def group(self) -> str:
"""
Returns the name of the group this algorithm belongs to. This string
should be localised.
"""
return "PASIG II"
def groupId(self) -> str:
"""
Returns the unique ID of the group this algorithm belongs to. This
string should be fixed for the algorithm, and must not be localised.
The group id should be unique within each provider. Group id should
contain lowercase alphanumeric characters only and no spaces or other
formatting characters.
"""
return "pasig_II"
def shortHelpString(self) -> str:
"""
Returns a localised short helper string for the algorithm. This string
should provide a basic description about what the algorithm does and the
parameters and outputs associated with it.
"""
descripcion = """
La herramienta calcula el NDVI (Normalized Difference Vegetation Index).
NDVI es usado para cuantificar la humedad en la vegetación y es útil para
comprender la densidad de la vegetación y evaluar cambios en la salud de
las plantas.
Esta herramienta funciona con imágenes de Landsat 8 o 9, y usa la siguiente
ecuación:
NDVI = (Band 5 – Band 4) / (Band 5 + Band 4)
Donde:
Band 4 es la banda 4, Rojo (Red)
Band 5 es la banda 5, Infrarrojo cercano (Near Infrared o NIR)
"""
return descripcion
def initAlgorithm(self, config=None):
"""
Here we define the inputs and output of the algorithm, along
with some other properties.
"""
#Capa de entrada con la banda 4
self.addParameter(QgsProcessingParameterRasterLayer('b4', 'Banda 4 de Landsat 8 o 9', defaultValue=None))
#Capa de entrada con la banda 5
self.addParameter(QgsProcessingParameterRasterLayer('b5', 'Banda 5 de Landsat 8 o 9', defaultValue=None))
#Capa de salida con el NDVI
self.addParameter(QgsProcessingParameterRasterDestination('ndvi', 'Capa ráster con el NDVI', createByDefault=True, defaultValue=None))
def processAlgorithm(self, parameters, context, model_feedback):
"""
Here is where the processing itself takes place.
"""
feedback = QgsProcessingMultiStepFeedback(1, model_feedback)
outputs = {}
results = {}
print(parameters)
#Parametros del algoritmo (calculadora ráster)
alg_params = {'EXPRESSION':f'("{parameters["b5"]}@1" - "{parameters["b4"]}@1") / ("{parameters["b5"]}@1" + "{parameters["b4"]}@1")',
'LAYERS':[parameters["b4"], parameters["b5"]],
'CELLSIZE': 0,
'EXTENT':None,
'CRS':None,
'OUTPUT':parameters["ndvi"]}
#Ejecución del algoritmo (calculadora ráster)
outputs['ndvi'] = processing.run('qgis:rastercalculator', alg_params, context=context, feedback=feedback)
print(outputs)
results['ndvi'] = outputs['ndvi']['OUTPUT']
print(results)
return results
def createInstance(self):
return self.__class__()
Sección 1 · Para qué sirve cada biblioteca importada
from qgis.core import QgsProcessingAlgorithm #Clase de la que se hereda en nuestra herramienta
from qgis.core import QgsProcessingParameterRasterLayer #Para cargar capas raster al diccionario de parámetros
from qgis.core import QgsProcessingParameterRasterDestination #Para guardar una salida ráster
from qgis.core import QgsProcessingMultiStepFeedback #Para mostrar el progreso de la operación
import processing #Para usar en "processing.run()"
QgsProcessingAlgorithm: es la clase base de la que debe heredar cualquier herramienta de procesamiento personalizada. Sin esta herencia, QGIS no reconocería nuestra clase como una herramienta ejecutable.QgsProcessingParameterRasterLayer: se usa para declarar, dentro deinitAlgorithm, cada parámetro de entrada que el usuario debe llenar con una capa ráster (en este ejercicio, las bandas 4 y 5).QgsProcessingParameterRasterDestination: se usa para declarar el archivo ráster de salida (aquí, la capa con el NDVI calculado).QgsProcessingMultiStepFeedback: permite reportar el progreso de la ejecución al usuario y detectar si canceló el proceso, aunque en este ejercicio solo haya un paso.import processing: a diferencia de los ejercicios anteriores (que usabanfrom qgis import processing), aquí se importa el móduloprocessingdirectamente. Ambas formas permiten usarprocessing.run(...)para ejecutar cualquier herramienta de la Caja de herramientas desde código.
Sección 2 · Para qué sirve cada método de la clase
La clase Landsat_NDVI incluye un docstring que
resume sus 8 métodos. A continuación se explica el propósito de cada uno:
name(): devuelve el identificador interno único de la herramienta ("landsat_ndvi"), usado internamente por QGIS.displayName(): devuelve el nombre visible para el usuario en la interfaz de QGIS ("NDVI con imágenes Landsat 8 o 9").group(): indica en qué grupo de la Caja de herramientas aparecerá esta herramienta ("PASIG II").groupId(): es el identificador interno de ese mismo grupo ("pasig_II"), usado por QGIS para relacionar herramientas del mismo grupo.shortHelpString(): devuelve un texto explicativo que QGIS muestra dentro de la ventana de la herramienta, describiendo qué hace el NDVI y qué representan sus bandas de entrada.initAlgorithm(): declara los parámetros de entrada (bandas 4 y 5) y de salida (capa NDVI) que el usuario debe proporcionar antes de ejecutar la herramienta.processAlgorithm(): contiene la lógica real del cálculo: arma la expresión matemática del NDVI y la ejecuta con la calculadora ráster de QGIS.createInstance(): le indica a QGIS cómo crear una nueva copia de esta herramienta cada vez que se ejecuta.
Sección 3 · Declaración de parámetros (initAlgorithm)
def initAlgorithm(self, config=None):
#Capa de entrada con la banda 4
self.addParameter(QgsProcessingParameterRasterLayer('b4', 'Banda 4 de Landsat 8 o 9', defaultValue=None))
#Capa de entrada con la banda 5
self.addParameter(QgsProcessingParameterRasterLayer('b5', 'Banda 5 de Landsat 8 o 9', defaultValue=None))
#Capa de salida con el NDVI
self.addParameter(QgsProcessingParameterRasterDestination('ndvi', 'Capa ráster con el NDVI', createByDefault=True, defaultValue=None))
self.addParameter(QgsProcessingParameterRasterLayer('b4', ...)): declara la banda 4 (rojo) como parámetro de entrada, identificada internamente con la llave'b4'.self.addParameter(QgsProcessingParameterRasterLayer('b5', ...)): declara la banda 5 (infrarrojo cercano) como segundo parámetro de entrada.self.addParameter(QgsProcessingParameterRasterDestination('ndvi', ...)): declara el archivo de salida donde se guardará la capa NDVI calculada.
Sección 4 · Cálculo del NDVI (processAlgorithm)
def processAlgorithm(self, parameters, context, model_feedback):
feedback = QgsProcessingMultiStepFeedback(1, model_feedback)
outputs = {}
results = {}
print(parameters)
#Parametros del algoritmo (calculadora ráster)
alg_params = {'EXPRESSION':f'("{parameters["b5"]}@1" - "{parameters["b4"]}@1") / ("{parameters["b5"]}@1" + "{parameters["b4"]}@1")',
'LAYERS':[parameters["b4"], parameters["b5"]],
'CELLSIZE': 0,
'EXTENT':None,
'CRS':None,
'OUTPUT':parameters["ndvi"]}
#Ejecución del algoritmo (calculadora ráster)
outputs['ndvi'] = processing.run('qgis:rastercalculator', alg_params, context=context, feedback=feedback)
print(outputs)
results['ndvi'] = outputs['ndvi']['OUTPUT']
print(results)
return results
feedback = QgsProcessingMultiStepFeedback(1, model_feedback): aquí solo se declara 1 paso, porque todo el cálculo ocurre en una sola llamada a la calculadora ráster.outputs = {}/results = {}: los mismos diccionarios vacíos vistos en ejercicios anteriores, para resultados intermedios y finales.print(parameters): imprime en la consola de Python de QGIS el contenido deparameters, útil para depurar (revisar qué valores llegaron) mientras se construye la herramienta.'EXPRESSION': f'("{parameters["b5"]}@1" - "{parameters["b4"]}@1") / (...)': arma, como texto, la fórmula del NDVI. El símbolo@1indica "la banda número 1 de esa capa" (el formato que espera la calculadora ráster de QGIS).'LAYERS': [parameters["b4"], parameters["b5"]]: lista de capas que participan en la expresión, para que la calculadora ráster sepa a cuáles hace referencia la fórmula.'CELLSIZE': 0: al dejarlo en 0, QGIS calcula automáticamente el tamaño de celda adecuado a partir de las capas de entrada.'EXTENT': None/'CRS': None: al dejarlos enNone, QGIS usa automáticamente la extensión y el sistema de coordenadas de las capas de entrada.'OUTPUT': parameters["ndvi"]: el resultado se guarda directamente en el destino final, ya que es el único paso del modelo.outputs['ndvi'] = processing.run('qgis:rastercalculator', alg_params, ...): ejecuta la herramienta nativa "Calculadora ráster" con la expresión armada.results['ndvi'] = outputs['ndvi']['OUTPUT']yreturn results: entrega el resultado final a QGIS.