En el ejercicio de hoy vamos a ver un ejemplo de cómo recortar, utilizando la herramienta Clip ráster by mask, todos los ficheros ráster que tengamos almacenados en una carpeta concreta, automatizando el proceso con PyQGIS.
Para el desarrollo del ejercicio vamos a utilizar los ficheros ráster en formato *tif de la Red de Información Ambiental de Andalucía (REDIAM). En concreto, vamos a descargar los ficheros ráster de precipitación anual desde 1940 hasta 2019 para toda Andalucía. El enlace para descargar la carpeta completa.
Como ejemplo voy a seleccionar solamente los ficheros desde 2010, aunque si quisiéramos recortar todos los ráster descargados tan solo había que colocarlos en la misma carpeta. Para almacenar los ficheros de ejemplo he creado una carpeta llamada “raster” dentro de nuestro directorio de trabajo.
Para recortar los ráster vamos a utilizar una única máscara, que va a ser en este caso un fichero vectorial de la provincia de Cádiz. Para generarlo, podemos descargar del Centro de Descargas del IGN (CNIG), dentro de “Información Geográfica de Referencia”, los recintos provinciales. De esa capa extraeremos mediante una selección por atributos la provincia de Cádiz. Exportaremos la capa (Export – Save Features As) con el nombre “cadiz.shp” dentro de nuestro directorio de trabajo.
Para trabajar el código vamos a utilizar para mayor comodidad la consola de Python de QGIS Para acceder a ella: “Ctrl + Alt + P” y hacer click en el icono señalado.
Creamos un nuevo fichero .py y añadimos en primer lugar las librerías que nos van a hacer falta para ejecutar la herramienta, acceder a los directorios y crear carpetas.
/** from osgeo import gdal import os from pathlib import Path */
Nombramos una nueva variable “basepath” que contendrá el directorio principal sobre el que vamos a trabajar. En mi caso, completo la ruta de la carpeta de usuario (“Path().home()”) que tengo definida añadiéndole con el método .joinpath() la ruta sobre la que quiero trabajar (“carpeta de usuario\\Documents\\entradas\\preci”).
Tras definir el directorio, creo las variables que van a contener los ficheros ráster (en este caso la carpeta “raster” que hemos creado dentro de la ruta del “basepath”) y el fichero vectorial (dentro del “basepath”, el fichero “cadiz.shp”).
/** basepath = Path().home().joinpath("Documents","entradas","preci") rasterPath = basepath.joinpath("raster") vectorPath = basepath.joinpath("cadiz.shp") */
Creamos una variable denominada “dir_recorte” para crear el directorio en el que se van a almacenar los ráster recortados y establecemos una condición para que, si existe la carpeta “recortes” en ese directorio no haga nada, pero que si no existe una carpeta con ese nombre nos la genere automáticamente con el método .mkdir().
/** dir_recorte = rasterPath.joinpath('recortes') if not os.path.exists(dir_recorte): os.mkdir(dir_recorte) */
A continuación, creamos un bucle “for” para iterar la carpeta que contiene los ráster. Utilizamos el método “glob” para buscar aquellos ficheros con el formato .tif”. Dentro del bucle definimos la ruta de salida en cada momento de la iteración, dependiendo del ráster que se esté utilizando como entrada. El nombre que tomará cada ráster de salida será del tipo “Cadiz_[lyr-name]”, siendo “lyr.name” el nombre del ráster de entrada, incluyendo su extensión (.tif). Utilizamos el método .format(), el cual devuelve el argumento convertido en tipo texto.
Dentro del bucle también definimos los parámetros del algoritmo de “Clip raster by mask layer”. Hay que destacar que como ‘INPUT’ se va a utilizar el fichero ráster en cada momento de la iteración (variable “lyr”), que como ‘MASK’ se va a utilizar la capa vectorial, y que el ‘OUTPUT’ va a ser la ruta definida en el “output_path”. Añadimos al final de estas variables el método .as_posix(), el cual representa la cadena utilizando barras invertidas (/).
Tras los parámetros creamos la variable que almacenará el resultado, donde llamaremos al algoritmo con su ID («gdal:cliprasterbymasklayer») y con los parámetros como segundo argumento. Por último, añadiremos las capas resultantes al proyecto con QgsProject.instance().addMapLayer(). El nombre de las capas en el proyecto también estarán condicionadas por el bucle, por lo que el nombre también cambiará en función del ráster de entrada. Esto se puede generar utilizando .stem, el cual devuelve el nombre del fichero (en este caso el ráster de entrada) sin la extensión.
/** for lyr in rasterPath.glob("*.tif"): output_path = dir_recorte.joinpath("Cadiz_{}".format(lyr.name)) parameters = { 'ALPHA_BAND': False, 'CROP_TO_CUTLINE': True, 'DATA_TYPE': 0, 'INPUT': lyr.as_posix(), 'KEEP_RESOLUTION': False, 'MASK': vectorPath.as_posix(), 'MULTITHREADING': False, 'OPTIONS': '', 'OUTPUT': output_path.as_posix(), 'SET_RESOLUTION': False, 'SOURCE_CRS': None, 'TARGET_CRS': None, 'X_RESOLUTION': None, 'Y_RESOLUTION': None } resultado = processing.run("gdal:cliprasterbymasklayer", parameters) QgsProject.instance().addMapLayer(QgsRasterLayer(resultado['OUTPUT'], output_path.stem, 'gdal')) */
Como resultado del ejercicio tendremos en nuestra carpeta “recortes” los 10 ráster de precipitación (2010 a 2019) de la provincia de Cádiz.
Os comparto el código completo.
/** from osgeo import gdal import os from pathlib import Path basepath = Path().home().joinpath("Documents","entradas","preci") rasterPath = basepath.joinpath("raster") vectorPath = basepath.joinpath("cadiz.shp") dir_recorte = rasterPath.joinpath('recortes') if not os.path.exists(dir_recorte): os.mkdir(dir_recorte) for lyr in rasterPath.glob("*.tif"): output_path = dir_recorte.joinpath("Cadiz_{}".format(lyr.name)) parameters = { 'ALPHA_BAND': False, 'CROP_TO_CUTLINE': True, 'DATA_TYPE': 0, 'INPUT': lyr.as_posix(), 'KEEP_RESOLUTION': False, 'MASK': vectorPath.as_posix(), 'MULTITHREADING': False, 'OPTIONS': '', 'OUTPUT': output_path.as_posix(), 'SET_RESOLUTION': False, 'SOURCE_CRS': None, 'TARGET_CRS': None, 'X_RESOLUTION': None, 'Y_RESOLUTION': None } resultado = processing.run("gdal:cliprasterbymasklayer", parameters) QgsProject.instance().addMapLayer(QgsRasterLayer(resultado['OUTPUT'], output_path.stem, 'gdal')) */
Buenas tardes,
Estoy intentando cortar a través de Pyqgis la orto Expedita del IGN (.ecw). Tengo en una carpeta los shapefiles que servirán para el recorte de la orto, pero al adaptar el código a mis rutas, me da un error en la importación
from osgeo import gdal
El error que me devuelve la consola es:
Traceback (most recent call last):
File «C:\QGIS31~1.14\apps\Python39\lib\code.py», line 90, in runcode
exec(code, self.locals)
File «», line 1, in
File «», line 16, in
NameError: name ‘osmkdir’ is not defined
¿Dónde esta el error?¿Me podrías ayudar?
Muchas gracias
Buenas tardes Juanma,
El error parece ser debido a un error de sintaxis en el código. En la línea 16 de tu código te falta añadirle el punto en os.mkdir().
Un saludo,
Manuel
Muchas gracias, es verdad me faltaba el punto en os.mkdir(), pero aun me sigue saliendo el error en la linea 1 y 15, que es dónde importo la librería y donde añado el condicional.
Traceback (most recent call last):
File «C:\QGIS31~1.14\apps\Python39\lib\code.py», line 90, in runcode
exec(code, self.locals)
File «», line 1, in
File «», line 15, in
FileNotFoundError: [WinError 3] El sistema no puede encontrar la ruta especificada: ‘C:\\Users\\Lenovo\\QGis\\Prueba_1\\Orto\\recortes’
¿Te añado el código completo, por si puedes echarle un vistazo y ver dónde esta el error?
Muchísimas gracias,
Un saludo.
from osgeo import gdal
import os
from pathlib import Path
basepath = Path().home().joinpath(«QGis»,»Prueba_1″)
rasterPath = basepath.joinpath(«Orto»)
vectorPath = basepath.joinpath(«ID_EXPED_6368.shp»)
dir_recorte = rasterPath.joinpath(‘recorte’)
if not os.path.exists(dir_recorte):
os.mkdir(dir_recorte)
for lyr in rasterPath.glob(«*.ecw»):
output_path = dir_recorte.joinpath(«ID_EXPED_{}».format(lyr.name))
parameters = {
‘ALPHA_BAND’: False,
‘CROP_TO_CUTLINE’: True,
‘DATA_TYPE’: 0,
‘INPUT’: lyr.as_posix(),
‘KEEP_RESOLUTION’: False,
‘MASK’: vectorPath.as_posix(),
‘MULTITHREADING’: False,
‘OPTIONS’: »,
‘OUTPUT’: output_path.as_posix(),
‘SET_RESOLUTION’: False,
‘SOURCE_CRS’: None,
‘TARGET_CRS’: None,
‘X_RESOLUTION’: None,
‘Y_RESOLUTION’: None
}
resultado = processing.run(«gdal:cliprasterbymasklayer», parameters)
QgsProject.instance().addMapLayer(QgsRasterLayer(resultado[‘OUTPUT’], output_path.stem, ‘gdal’))
Buenas tardes Juanma,
El error se debe a que no encuentra la ruta de la carpeta de salida. A la hora de crear la carpeta «recorte», es necesario que las demás carpetas que conforman la ruta («QGis», «Prueba_1» y «Orto») existan. Por favor, comprueba que estas carpetas existen tal y como las escribes en el código.
Por otro lado, una vez que consigas solucionar este apartado, para que no tengas errores posteriores, es necesario que el código esté bien indentado (el bucle «for», que debe incluir tanto el «output_path» como los parámetros, y el condicional «if», que debe incluir el «os.mkdir()»).
Por lo demás, el código parece que es correcto (imagino que los caracteres ««» que aparecen en el código vienen derivados de una incompatibilidad en el formato al copiar aquí el código).
P.D.: tengo mis dudas de que la herramienta funcione correctamente con los ráster en formato «.ecw». Te recomiendo que antes los conviertas a formato .»tif». Por si te quisieras animar a implementar en el código esta función de convertir previamente los ráster, GDAL trae una herramienta que se llama «translate» («gdal:translate»).
Espero haber sido de ayuda.
Un saludo,
Manuel
Tienes toda la razón. Las carpetas estaban bien nombradas, pero la ruta era demasiado larga. Al cambiarla de ubicación, el problema se solucionó.
Igualmente, al transformar mi orto a «.tif» el código corre bien y se ejecuta sin fallo alguno.
Ahora el único problema que encuentro es que, ¿dónde se guardan los recortes? La carpeta que crea el código «recortes» se encuentra ubicada dónde se le indica, pero no tiene ningún archivo en su interior.
Un saludo.
Buenos días Juanma,
Si se ejecuta bien el código pero no saca los recortes, es porque hay algo raro a la hora de la ejecución de la herramienta de QGIS.
Recuerda que el código lo que hace es recortar múltiples ficheros ráster utilizando un único shapefile.
Es importante que tanto los ráster como el shapefile de recorte se encuentren en el mismo sistema de coordenadas y que el shapefile de recorte tenga una extensión espacial menor a la de los ráster a recortar (es decir, que sea un área que esté contenida en los ráster).
Espero que con esto puedas solucionarlo.
Un saludo,
Manuel
¡Perfecto! Solucionado. Muchísimas gracias.
Mi problema es que había dividido el shape original en shapefiles independiente para cada uno de los polígonos que forman parte del shape original. Necesito que cada polígono tenga su raster independiente. Tendré que hacer otro bucle for en el «vectorPath» para que itere cada uno de los shapes.
Un saludo.
Juanma.