rasterio: biblioteca para lectura, escritura y análisis de datos raster#

Abrir en Colab

Introducción#

rasterio es una biblioteca de Python que permite leer, escribir y analizar datos raster. Construida sobre GDAL (Geospatial Data Abstraction Library), proporciona una interfaz para trabajar con imágenes satelitales, modelos digitales de elevación (DEM) y otros tipos de datos raster.

Instalación#

En ambientes locales (ej. conda)#

Se recomienda actualizar primero conda y mamba.

# Actualizar conda y mamba
conda update conda
conda update -c conda-forge mamba

rasterio puede instalarse con pip, conda o mamba, desde la línea de comandos del sistema operativo. Solo es necesario hacerlo de una forma. Se recomienda instalarla junto con el paquete fiona (para leer y escribir archivos geoespaciales).

# Instalar rasterio y sus bibliotecas asociadas con pip
pip install rasterio fiona

# Instalar rasterio y sus bibliotecas asociadas con conda
conda install -c conda-forge rasterio fiona

# Instalar rasterio y sus bibliotecas asociadas con mamba
mamba install -c conda-forge rasterio fiona

En la nube (ej. Google Colab)#

# Con pip
pip install rasterio fiona

Carga#

# Carga de rasterio
import rasterio

# Módulo de rasterio para graficar datos raster
import rasterio.plot

# Biblioteca para datos geoespaciales vectoriales
import geopandas as gpd

# Biblioteca para álgebra lineal
import numpy as np

# Biblioteca de graficación
import matplotlib.pyplot as plt

Operaciones básicas#

Lectura de datos#

La función rasterio.open() abre un conjunto de datos raster para lectura o escritura.

En el siguiente bloque de código, se carga un conjunto de datos raster correspondiente a la altitud de Costa Rica, proveniente del sitio WorldClim de información climática.

# Lectura de datos de altitud
altitud = rasterio.open(
    'https://github.com/gf0657-programacionsig/2024-ii/raw/refs/heads/main/datos/worldclim/altitud.tif'
)

print(altitud)
<open DatasetReader name='https://github.com/gf0657-programacionsig/2024-ii/raw/refs/heads/main/datos/worldclim/altitud.tif' mode='r'>

En el bloque de código anterior, rasterio.open() retorna un objeto de la clase DatasetReader, el cual permite interactuar con un archivo raster. El objeto altitud no almacena el contenido del archivo GeoTIFF, solamente un “puntero” a su ubicación.

Consulta de propiedades y metadatos#

Una vez que un conjunto de datos ha sido abierto, pueden recuperarse propiedades y metadatos como su CRS, resolución, límites, bandas y tipo de datos.

# Nombre de la fuente de datos
altitud.name
'https://github.com/gf0657-programacionsig/2024-ii/raw/refs/heads/main/datos/worldclim/altitud.tif'
# Modo de apertura de la fuente de datos
# (ej. r = lectura, w = escritura)
altitud.mode
'r'
# CRS
altitud.crs
CRS.from_epsg(4326)
# Resolución (tamaño de cada celda)
altitud.res
(0.008333333333333316, 0.008333333333333337)
# Límites
altitud.bounds
BoundingBox(left=-87.1, bottom=5.5, right=-82.55833333333334, top=11.216666666666669)
# Metadatos
altitud.meta
{'driver': 'GTiff',
 'dtype': 'int16',
 'nodata': -32768.0,
 'width': 545,
 'height': 686,
 'count': 1,
 'crs': CRS.from_epsg(4326),
 'transform': Affine(0.008333333333333316, 0.0, -87.1,
        0.0, -0.008333333333333337, 11.216666666666669)}

Graficación#

La función rasterio.plot.show() despliega un raster o una banda de un raster.

# Mapa de altitud
rasterio.plot.show(altitud)
../../_images/25dec7edc0f4617d14bc1281776541d018efc146c7e4f8e7596d262f055650eb.png
<Axes: >

El mapa anterior se puede personalizar con diversas opciones de la interfaz Axes de matplotlib.

fig, ax = plt.subplots(figsize=(8, 8))

# Mapa de altitud
rasterio.plot.show(
    altitud, 
    cmap="terrain", # colores
    ax=ax, 
    title="Altitud de Costa Rica"
)

# Agregar una leyenda
cbar = fig.colorbar(ax.images[0], ax=ax, shrink=0.8)
cbar.set_label('Altitud (m)')

# Limitar el rango de los ejes
ax.set_xlim(-86.0, -82.5)
ax.set_ylim(7.9, 11.4)

plt.show()
../../_images/5c50c54e0c963d49266d107f624346738ef39827ce1377ff9185f0f9f6db4ff0.png

La lista completa de paletas de colores de matplotlib puede consultarse en Colormap reference – matplotlib documentation.

Ejercicios

  1. Programe mapas para las siguientes capas raster:

Manejo de múltiples bandas#

# Lectura de imagen Sentinel
imagen = rasterio.open(
    'https://github.com/gf0657-programacionsig/2024-ii/raw/refs/heads/main/datos/sentinel/gandoca-20240105.tif'
)
print(imagen)
<open DatasetReader name='https://github.com/gf0657-programacionsig/2024-ii/raw/refs/heads/main/datos/sentinel/gandoca-20240105.tif' mode='r'>
# Metadatos
imagen.meta
{'driver': 'GTiff',
 'dtype': 'float32',
 'nodata': None,
 'width': 128,
 'height': 154,
 'count': 7,
 'crs': CRS.from_epsg(8857),
 'transform': Affine(10.0, 0.0, -7868055.0,
        0.0, -10.0, 1235575.0)}

Estas son las bandas:

Banda 1: Aerosol de costa
Banda 2: Azul
Banda 3: Verde
Banda 4: Rojo
Banda 5: Infrarrojo cercano (NIR)
Banda 6: Infrarrojo de onda corta 1 (SWIR1)
Banda 7: Infrarrojo de onda corta 2 (SWIR2)

# Lista de nombres de bandas
nombres_bandas = [
    "aerosol de costa", 
    "azul", 
    "verde", 
    "roja", 
    "NIR", 
    "SWIR1", 
    "SWIR2"
]

Visualización de una banda#

# Visualización de una banda
rasterio.plot.show((imagen, 5), cmap="Greys_r")
../../_images/d3e1bf95518d387d71ce3504c10adc97b28748407f802fe210f01a6c8039c859.png
<Axes: >

Visualización de múltiples bandas#

fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(8, 10))
axes = axes.flatten()

for banda in range(1, imagen.count + 1):
    datos = imagen.read(banda)
    ax = axes[banda - 1]
    im = ax.imshow(datos, cmap="gray")
    ax.set_title(f"Banda {nombres_bandas[banda - 1]}")
    fig.colorbar(im, ax=ax, label="Reflectancia", shrink=0.8)

plt.tight_layout()
plt.show()
../../_images/b5109919572dd6d4e9abf9093906648ae9be9a6413d801423dd641ba0d09f06a.png

Combinación de bandas#

red_band = imagen.read(4).astype('float32')
green_band = imagen.read(3).astype('float32')
nir_band = imagen.read(5).astype('float32')

# Manejar valores NaN
nir_band = np.nan_to_num(nir_band, nan=nir_band.min())
red_band = np.nan_to_num(red_band, nan=red_band.min())
green_band = np.nan_to_num(green_band, nan=green_band.min())

# Normalizar las bandas
nir_norm = (nir_band - nir_band.min()) / (nir_band.max() - nir_band.min())
red_norm = (red_band - red_band.min()) / (red_band.max() - red_band.min())
green_norm = (green_band - green_band.min()) / (green_band.max() - green_band.min())

# Verificar que los valores están entre 0 y 1
nir_norm = nir_norm.clip(0, 1)
red_norm = red_norm.clip(0, 1)
green_norm = green_norm.clip(0, 1)

# Arreglo de bandas
rgb = np.dstack((red_norm, green_norm, nir_norm))

# Mapa
plt.figure(figsize=(8, 8))
plt.imshow(rgb)
plt.title("Bandas NIR, roja y verde combinadas")
plt.axis('off')
plt.show()
../../_images/0eff5f24b43a79f51e06702aad6622d37be0803ec0e6d6a6a36d8d8993695849.png

Cálculo del NDVI#

# Cálculo del NDVI: NDVI = (NIR - roja) / (NIR + roja)
ndvi = (nir_band - red_band) / (nir_band + red_band)
ndvi = ndvi.clip(-1, 1)

plt.figure(figsize=(8, 8))
plt.imshow(ndvi, cmap="RdYlGn", vmin=-1, vmax=1)
plt.colorbar(label="NDVI", shrink=0.5)
plt.title("NDVI")
plt.show()
../../_images/445a3aff1c1b111650b9345e4ae3d26fc4dff37a81d9738c2718eee292b52d4b.png

Recursos de interés#

Colormap reference – matplotlib documentation