diff --git a/CONDUCT-es-419.md b/CONDUCT-es.md
similarity index 100%
rename from CONDUCT-es-419.md
rename to CONDUCT-es.md
diff --git a/CONTRIBUTING-es-419.md b/CONTRIBUTING-es.md
similarity index 100%
rename from CONTRIBUTING-es-419.md
rename to CONTRIBUTING-es.md
diff --git a/book/es-419/03_Using_NASA_EarthData/01_Using_OPERA_DIST_Products.md b/book/es-419/03_Using_NASA_EarthData/01_Using_OPERA_DIST_Products.md
deleted file mode 100644
index 81569dc..0000000
--- a/book/es-419/03_Using_NASA_EarthData/01_Using_OPERA_DIST_Products.md
+++ /dev/null
@@ -1,343 +0,0 @@
----
-jupyter:
- jupytext:
- text_representation:
- extension: .md
- format_name: markdown
- format_version: "1.3"
- jupytext_version: 1.16.4
- kernelspec:
- display_name: Python 3 (ipykernel)
- language: python
- name: python3
----
-
-# Utilización de los productos OPERA DIST
-
-
-
-## El proyecto OPERA
-
-
-
-
-
-
-
-
-
-Del proyecto [Observational Products for End-Users from Remote Sensing Analysis (OPERA)](https://www.jpl.nasa.gov/go/opera) (en español, Productos de Observación para Usuarios Finales a partir del Analisis por Teledetección):
-
-> Iniciado en abril del 2021, el proyecto OPERA del _Jet Propulsion Laboratory_ (JPL) (en español, Laboratorio de Propulsión a Chorro) recopila datos satelitales ópticos y de radar para generar seis conjuntos de productos:
->
-> - un conjunto de productos sobre la extensión de las aguas de la superficie terrestre a escala casi mundial
-> - un conjunto de productos de Alteraciones de la Superficie terrestre a escala casi mundial
-> - un producto con Corrección Radiométrica del Terreno a escala casi mundial
-> - un conjunto de productos _Coregistered Single Look Complex_ para Norteamérica
-> - un conjunto de productos de Desplazamiento para Norteamérica
-> - un conjunto de productos de Movimiento Vertical del Terreno en Norteamérica
-
-Es decir, OPERA es una iniciativa de la National Aeronautics and Space Administration (NASA, en español, Administración Nacional de Aeronáutica y del Espacio) que toma, por ejemplo, datos de teledetección óptica o radar recopilados desde satélites, y genera una variedad de conjuntos de datos preprocesados para uso público. Los productos de OPERA no son imágenes de satélite sin procesar, sino el resultado de una clasificación algorítmica para determinar, por ejemplo, qué regiones terrestres contienen agua o dónde se ha modificado la vegetación. Las imágenes de satélite sin procesar se recopilan a partir de mediciones realizadas por los instrumentos a bordo de las misiones de los satélites Sentinel-1 A/B, Sentinel-2 A/B y Landsat-8/9 (de ahí el término _Harmonized Landsat-Sentinel_" (HLS) (en español, Landsat-Sentinel Armonizadas) para en numerosas descripciones de productos).
-
-
-
-***
-
-## El producto OPERA _Land Surface Disturbance_ (DIST) (en español, Perturbación de la superficie terrestre)
-
-
-
-Uno de estos productos de datos de OPERA es el producto DIST (descrito con más detalle en la especificación del producto [OPERA DIST HLS](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)).
-Los productos DIST mapean la _perturbación de la vegetación_ (en concreto, la pérdida de cubierta vegetal por píxel HLS siempre que haya una disminución indicada) a partir de escenas armonizadas Landsat-8 y Sentinel-2 A/B (HLS). Una de las aplicaciones de estos datos es cuantificar los daños causados por los _incendios forestales_. El producto DIST_ALERT se publica a intervalos regulares (al igual que las imágenes HLS, aproximadamente cada 12 días en un determinado mosaico/región). El producto DIST_ANN resume las mediciones de las alteraciones a lo largo de un año.
-
-Los productos DIST cuantifican los datos de reflectancia de la superficie (RS) (en inglés, Surface Reflectance, SR) adquiridos a partir de imágenes terrestres operacionales _Operational Land Imager_ (OLI) (en español, Generador de Imágenes Terrestres Operacional) a bordo del satélite de teledetección Landsat-8 y del _Multi-Spectral Instrument_ (MSI) (en español, Instrumento Multiespectral) a bordo del satélite de teledetección Sentinel-2 A/B. Los productos de datos HLS DIST son archivos de tipo ráster, cada uno de ellos asociado a mosaicos de la superficie terrestre. Cada mosaico se representa mediante coordenadas cartográficas proyectadas alineadas con el [Sistema de Referencia de Cuadrículas Militares (MGRS, por sus siglas en inglés de _Military Grid Reference System_)](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). Cada mosaico se divide en 3,660 filas y 3,660 columnas con un espaciado de píxeles de 30 metros (así que un mosaico es de $109.8\,\mathrm{km}$ largo en cada lado). Los mosaicos vecinos se solapan 4.900 metros en cada dirección (los detalles se describen detalladamente en la [especificación de producto DIST](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)).
-
-Los productos OPERA DIST se distribuyen como [GeoTIFFs optimizados para la nube](https://www.cogeo.org/); en la práctica, esto significa que las diferentes bandas se almacenan en archivos de formato TIFFs (TIFF, por sus siglas en inglés, _Tagged Image File Format_) distintos. La especificación TIFF permite el almacenamiento de matrices multidimensionales en un único archivo. El almacenamiento de bandas distintas en diferentes archivos TIFF permite que estos se descarguen de forma independiente.
-
-
-
-***
-
-
-
-## Banda 1: Valor máximo de la anomalía de pérdida de vegetación (VEG_ANOM_MAX)
-
-
-
-
-
-Examina un archivo local con un ejemplo de datos DIST-ALERT. El archivo contiene la primera banda de datos de alteración: la _anomalía de pérdida máxima de vegetación_. Para cada píxel, se trata de un valor entre 0% y 100% que representa la diferencia porcentual entre la cobertura vegetal que se observa actualmente y un valor de referencia histórico. Es decir, un valor de 100 corresponde a una pérdida total de vegetación en un píxel y un valor de 0 corresponde a que no hubo pérdida de vegetación. Los valores de los píxeles se almacenan como enteros sin signo de 8 bits (UInt8) porque los valores de los píxeles solo deben oscilar entre 0 y 100. Un valor del píxel de 255 indica que faltan datos, es decir, que los datos HLS no pudieron determinar un valor máximo de anomalía en la vegetación para ese píxel. Por supuesto, el uso de datos enteros sin signo de 8 bits es mucho más eficiente para el almacenamiento y para la transmisión de datos a través de una red (en comparación con, por ejemplo, datos de punto flotante de 32 o 64 bits).
-
-Empieza importando las librerías necesarias. Observa que también estamos importando la clase `FixedTicker` de la librería Bokeh para hacer que los gráficos interactivos sean un poco más atractivos.
-
-
-
-```{code-cell} python editable=true jupyter={"source_hidden": false} slideshow={"slide_type": ""}
-# Notebook dependencies
-import warnings
-warnings.filterwarnings('ignore')
-from pathlib import Path
-import rioxarray as rio
-import geoviews as gv
-gv.extension('bokeh')
-import hvplot.xarray
-from bokeh.models import FixedTicker
-
-FILE_STEM = Path.cwd().parent if 'book' == Path.cwd().parent.stem else 'book'
-```
-
-
-
-Lee los datos de un archivo local `'OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-ANOM-MAX.tif'`. Antes de cargarlo, analiza los metadatos incluídos en el nombre del archivo.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-LOCAL_PATH = Path(FILE_STEM, 'assets/OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-ANOM-MAX.tif')
-filename = LOCAL_PATH.name
-print(filename)
-```
-
-
-
-Este nombre de archivo bastante largo incluye varios campos separados por caracteres de guión bajo (`_`). Podemos utilizar el método `str.split` de Python para ver más fácilmente los distintos campos.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-filename.split('_') # Use the Python str.split method to view the distinct fields more easily.
-```
-
-
-
-Los archivos de los productos OPERA tienen un esquema de nombres particular (como se describe en la [especificación de producto DIST](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)). En la salida anterior, puedes extraer ciertos metadatos para este ejemplo:
-
-1. _Product_: `OPERA`;
-2. _Level_: `L3` ;
-3. _ProductType_: `DIST-ALERT-HLS` ;
-4. _TileID_: `T10TEM` (cadena de caracteres que hace referencia a un mosaico del [MGRS](https://en.wikipedia.org/wiki/Military_Grid_Reference_System));
-5. _AcquisitionDateTime_: `20220815T185931Z` (cadena que representa una marca de tiempo GMT para la adquisición de los datos);
-6. _ProductionDateTime_ : `20220817T153514Z` (cadena que representa una marca de tiempo GMT para cuando se generó el producto de los datos);
-7. _Sensor_: `S2A` (identificador del satélite que adquirió los datos sin procesar: `L8` (Landsat-8), `S2A` (Sentinel-2 A) o `S2B` (Sentinel-2 B);
-8. _Resolution_: `30` (por ejemplo, píxeles de longitud lateral $30\mathrm{m}$);
-9. _ProductVersion_: `v0.1` (versión del producto); y
-10. _LayerName_: `VEG-ANOM-MAX`
-
-Ten en cuenta que la NASA utiliza nomenclaturas convencionales como [Earthdata Search](https://search.earthdata.nasa.gov) para extraer datos significativos de los [_SpatioTemporal Asset Catalogs_ (STACs)](https://stacspec.org/) (en español, Catálogos de Activos Espaciales y Temporales). Más adelante se utilizarán estos campo— en particular _TileID_ y _LayerName_; para filtrar los resultados de la búsqueda antes de recuperar los datos remotos.
-
-
-
-
-
-Sube los datos de este archivo local en un `DataArray`, que es un tipo de dato de Xarray, utilizando `rioxarray.open_rasterio`. Reetiqueta las coordenadas adecuadamente y extrae el CRS (sistema de referencia de coordenadas).
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-data = rio.open_rasterio(LOCAL_PATH)
-crs = data.rio.crs
-data = data.rename({'x':'longitude', 'y':'latitude', 'band':'band'}).squeeze()
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-data
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-crs
-```
-
-
-
-Antes de generar un gráfico, crea un mapa base utilizando mosaicos [ESRI](https://es.wikipedia.org/wiki/Esri).
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-# Creates basemap
-base = gv.tile_sources.ESRI.opts(width=750, height=750, padding=0.1)
-```
-
-
-
-También utiliza diccionarios para capturar la mayor parte de las opciones de trazado que utilizarás más adelante junto con `.hvplot.image`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-image_opts = dict(
- x='longitude',
- y='latitude',
- rasterize=True,
- dynamic=True,
- frame_width=500,
- frame_height=500,
- aspect='equal',
- cmap='hot_r',
- clim=(0, 100),
- alpha=0.8
- )
-layout_opts = dict(
- xlabel='Longitude',
- ylabel='Latitude'
- )
-```
-
-
-
-Por último, usa el método `DataArray.where` para filtrar los píxeles que faltan y los que no vieron ningún cambio en la vegetación; estos valores de píxeles serán reasignados como `nan` por lo que serán transparentes cuando el raster sea visualizado. También modifica ligeramente las opciones de `image_opts` y `layout_opts`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-veg_anom_max = data.where((data>0) & (data!=255))
-image_opts.update(crs=data.rio.crs)
-layout_opts.update(title=f"VEG_ANOM_MAX")
-```
-
-
-
-Estos cambios permiten generar una visualización útil.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-veg_anom_max.hvplot.image(**image_opts).opts(**layout_opts) * base
-```
-
-
-
-En el gráfico resultante, los píxeles blancos y amarillos corresponden a regiones en las que se ha producido cierta deforestación, pero no mucha. Por el contrario, los píxeles oscuros y negros corresponden a regiones que han perdido casi toda la vegetación.
-
-
-
-***
-
-## Banda 2: Fecha de alteración inicial de la vegetación (VEG_DIST_DATE)
-
-
-
-Los productos DIST-ALERT contienen varias bandas (tal como se resume en la [ especificación de productos DIST](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)). La segunda banda que se analiza es la _fecha de alteración inicial de la vegetación_ en el último año. Esta se almacena como un número entero de 16 bits (Int16).
-
-El archivo `OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-DATE.tif` se almacena localmente. La [especificación de productos DIST](\(https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf\)) describe cómo utilizar las convenciones para la denominación de archivos. Aquí destaca la _fecha y hora de adquisición_ `20220815T185931`, por ejemplo, casi las 7 p.m. (UTC) del 15 de agosto del 2022.
-
-Cargar y reetiqueta el `DataArray` como antes.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-LOCAL_PATH = Path(FILE_STEM, 'assets/OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-DATE.tif')
-data = rio.open_rasterio(LOCAL_PATH)
-data = data.rename({'x':'longitude', 'y':'latitude', 'band':'band'}).squeeze()
-```
-
-
-
-En esta banda en particular, el valor 0 indica que no ha habido alteraciones en el último año y -1 es un valor que indica que faltan datos. Cualquier valor positivo es el número de días desde el 31 de diciembre del 2020 en los que se midió la primera alteración en ese píxel. Filtrar los valores no positivos y conserva estos valores significativos utilizando `DataArray.where`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-veg_dist_date = data.where(data>0)
-```
-
-
-
-Examina el rango de valores numéricos en `veg_dist_date` utilizando DataArray.min`and`DataArray.max`. Ambos métodos ignorarán los píxeles que contengan `nan\` (por sus siglas en inglés de _Not-a-Number_) al calcular el mínimo y el máximo.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-d_min, d_max = int(veg_dist_date.min().item()), int(veg_dist_date.max().item())
-print(f'{d_min=}\t{d_max=}')
-```
-
-
-
-En este caso, los datos relevantes se encuentran entre 247 y 592. Recuerda que se trata del número de días transcurridos desde el 31 de diciembre del 2020, cuando se observó la primera alteración en el último año. Dado que estos datos se adquirieron el 15 de agosto del 2022, los únicos valores posibles estarían entre 227 y 592 días. Así que debes recalibrar los colores en la visualización
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-image_opts.update(
- clim=(d_min,d_max),
- crs=data.rio.crs
- )
-layout_opts.update(title=f"VEG_DIST_DATE")
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-veg_dist_date.hvplot.image(**image_opts).opts(**layout_opts) * base
-```
-
-
-
-Con este mapa de colores, los píxeles más claros mostraron algunos signos de deforestación hace cerca de un año. Por el contrario, los píxeles negros mostraron deforestación por primera vez cerca del momento de adquisición de los datos. Por tanto, esta banda es útil para seguir el avance de los incendios forestales a medida que arrasan los bosques.
-
-
-
-***
-
-
-
-## Banda 3: Estado de alteración de la vegetación (VEG_DIST_STATUS)
-
-
-
-
-
-Por último, se analiza una tercera banda de la familia de productos DIST-ALERT denominada _estado de alteración de la vegetación_. Estos valores de píxel se almacenan como enteros de 8 bits sin signo. Solo hay 6 valores distintos almacenados:
-
-- **0:** Sin alteración
-- **1:** Alteración provisional (**primera detección**) con cambio en la cubierta vegetal < 50%
-- **2:** Alteración confirmada (**detección recurrente**) con cambio en la cubierta vegetal < 50%
-- **3:** Alteración provisional con cambio en la cobertura vegetal ≥ 50%
-- **4:** Alteración confirmada con cambio en la cobertura vegetal ≥ 50%
-- **255**: Datos no disponibles
-
-El valor de un píxel se marca como cambiado provisionalmente cuando la pérdida de la cobertura vegetal (alteración) es observada por primera vez por un satélite. Si el cambio se vuelve a notar en posteriores adquisiciones HLS sobre dicho píxel, entonces el píxel se marca como confirmado.
-
-
-
-
-
-Se puede usar un archivo local como ejemplo de esta capa/banda particular de los datos DIST-ALERT. El código es el mismo que el anterior, pero observa que:
-
-- los datos filtrados reflejan los valores de píxel significativos para esta capa (por ejemplo, `data>0` and `data<5`), y
-- los valores del mapa de colores se reasignan en consecuencia (es decir, de 0 a 4).
--
-
-Observa el uso de `FixedTicker` en la definición de una barra de colores más adecuada para un mapa de color discreto (es decir, categórico).
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-LOCAL_PATH = Path(FILE_STEM, 'assets/OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-STATUS.tif')
-data = rio.open_rasterio(LOCAL_PATH)
-data = data.rename({'x':'longitude', 'y':'latitude', 'band':'band'}).squeeze()
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-veg_dist_status = data.where((data>0)&(data<5))
-image_opts.update(crs=data.rio.crs)
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-layout_opts.update(
- title=f"VEG_DIST_STATUS",
- clim=(0,4),
- colorbar_opts={'ticker': FixedTicker(ticks=[0, 1, 2, 3, 4])}
- )
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-veg_dist_status.hvplot.image(**image_opts).opts(**layout_opts) * base
-```
-
-
-
-Este mapa de colores continuo no resalta correctamente las características de este gráfico. Una mejor opción sería un mapa de colores _categórico_. Se mostrará como hacerlo en el próximo cuaderno computacional (con los productos de datos OPERA DSWx).
-
-
-
-***
diff --git a/book/es-419/03_Using_NASA_EarthData/03_Using_PySTAC.md b/book/es-419/03_Using_NASA_EarthData/03_Using_PySTAC.md
deleted file mode 100644
index 8cf249a..0000000
--- a/book/es-419/03_Using_NASA_EarthData/03_Using_PySTAC.md
+++ /dev/null
@@ -1,575 +0,0 @@
----
-jupyter:
- jupytext:
- text_representation:
- extension: .md
- format_name: markdown
- format_version: "1.3"
- jupytext_version: 1.16.2
- kernelspec:
- display_name: Python 3 (ipykernel)
- language: python
- name: python3
----
-
-# Uso de la API de PySTAC
-
-
-
-En el sitio web [Earthdata Search](https://search.earthdata.nasa.gov) de la NASA se puede buscar una gran cantidad de datos. El enlace anterior se conecta a una interfaz gráfica de usuario (GUI, por sus siglas en inglés de _Graphical User Interface_) para buscar en los [catálogos activos espaciotemporales (STACs, por sus siglas en inglés de _SpatioTemporal Asset Catalogs_)](https://stacspec.org/) al especificar un área de interés (AOI, por sus siglas en inglés de _Area of Interest_) y una _ventana temporal_ o un _intervalo de fechas_.
-
-En pos de la reproducibilidad, se busca que las personas usuarias sean capaces de buscar en los catálogos de activos de manera programática. Aquí es donde entra en juego la librería [PySTAC](https://pystac.readthedocs.io/en/stable/).
-
-
-
-***
-
-## Esquema de las etapas del análisis
-
-
-
-- Identificación de los parámetros de búsqueda
- - AOI, ventana temporal
- - Endpoint, proveedor, identificador del catálogo ("nombre corto")
-- Obtención de los resultados de la búsqueda
- - Exploración de datos, análisis para identificar características, bandas de interés
- - Almacenar los resultados en un DataFrame para facilitar la exploración
-- Explorar y refinar los resultados de la búsqueda
- - Identificar los granos de mayor valor
- - Filtrar los granos anómalos con una contribución mínima
- - Combinar los granos filtrados correspondientes en un DataFrame
- - Identificar el tipo de salida que se quiere obtener
-- Procesamiento de los datos para generar resultados relevantes
- - Descargar los granos relevantes en un tipo de dato DataArray de la libreria Xarray, apilados adecuadamente
- - Realizar los cálculos intermedios necesarios
- - Integrar los fragmentos de datos relevantes en una visualización
-
-
-
-***
-
-## Identificar los parámetros de búsqueda
-
-### Definir el AOI y el rango de fechas
-
-
-
-Comenzaremos tomando en cuenta un ejemplo concreto. [Las fuertes lluvias afectaron gravemente al sureste de Texas en mayo de 2024](https://www.texastribune.org/2024/05/03/texas-floods-weather-harris-county/), provocando [inundaciones y causando importantes daños materiales y humanos](https://www.texastribune.org/series/east-texas-floods-2024/).
-
-Como es usual, se requiere la importación de ciertas librerías relevantes. Las dos primeras celdas son familiares (relacionadas con las herramientas de análisis y la visualización de los datos que ya se examinaron). La tercera celda incluye la importación de la biblioteca `pystac_client` y de la biblioteca `gdal`, seguidas de algunos ajustes necesarios para utilizar la [Biblioteca de Abstracción de Datos Geoespaciales (GDAL, por sus siglas en inglés, Geospatial Data Abstraction Library)](https://gdal.org). Estos detalles en la configuración permiten que las sesiones de tu cuaderno computacional interactúen sin problemas con las fuentes remotas de datos geoespaciales.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-from warnings import filterwarnings
-filterwarnings('ignore')
-# data wrangling imports
-import numpy as np
-import pandas as pd
-import xarray as xr
-import rioxarray as rio
-import rasterio
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-# Imports for plotting
-import hvplot.pandas
-import hvplot.xarray
-import geoviews as gv
-from geoviews import opts
-gv.extension('bokeh')
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-# STAC imports to retrieve cloud data
-from pystac_client import Client
-from osgeo import gdal
-# GDAL setup for accessing cloud data
-gdal.SetConfigOption('GDAL_HTTP_COOKIEFILE','~/.cookies.txt')
-gdal.SetConfigOption('GDAL_HTTP_COOKIEJAR', '~/.cookies.txt')
-gdal.SetConfigOption('GDAL_DISABLE_READDIR_ON_OPEN','EMPTY_DIR')
-gdal.SetConfigOption('CPL_VSIL_CURL_ALLOWED_EXTENSIONS','TIF, TIFF')
-```
-
-
-
-A continuación, definiremos los parámetros de búsqueda geográfica para poder recuperar los datos correspondientes a ese evento de inundación. Esto consiste en especificar un _AOI_ y un _intervalo de fechas_.
-
-- El AOI se especifica como un rectángulo de coordenadas de longitud-latitud en una única 4-tupla con la forma
- $$({\mathtt{longitude}}_{\mathrm{min}},{\mathtt{latitude}}_{\mathrm{min}},{\mathtt{longitude}}_{\mathrm{max}},{\mathtt{latitude}}_{\mathrm{max}}),$$
- por ejemplo, las coordenadas de la esquina inferior izquierda seguidas de las coordenadas de la esquina superior derecha.
-- El intervalo de fechas se especifica como una cadena de la forma
- $${\mathtt{date}_{\mathrm{start}}}/{\mathtt{date}_{\mathrm{end}}},$$
- donde las fechas se especifican en el formato estándar `YYYY-MM-DD`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-# Center of the AOI
-livingston_tx_lonlat = (-95.09,30.69) # (lon, lat) form
-```
-
-
-
-Escribiremos algunas funciones cortas para encapsular la lógica de nuestros flujos de trabajo genéricos. Para el código de investigación, estas se colocarían en archivos de Python. Por practicidad, incrustaremos las funciones en este cuaderno y en otros para que puedan ejecutarse correctamente con dependencias mínimas.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-# simple utility to make a rectangle with given center of width dx & height dy
-def make_bbox(pt,dx,dy):
- '''Returns bounding-box represented as tuple (x_lo, y_lo, x_hi, y_hi)
- given inputs pt=(x, y), width & height dx & dy respectively,
- where x_lo = x-dx/2, x_hi=x+dx/2, y_lo = y-dy/2, y_hi = y+dy/2.
- '''
- return tuple(coord+sgn*delta for sgn in (-1,+1) for coord,delta in zip(pt, (dx/2,dy/2)))
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-# simple utility to plot an AOI or bounding-box
-def plot_bbox(bbox):
- '''Given bounding-box, returns GeoViews plot of Rectangle & Point at center
- + bbox: bounding-box specified as (lon_min, lat_min, lon_max, lat_max)
- Assume longitude-latitude coordinates.
- '''
- # These plot options are fixed but can be over-ridden
- point_opts = opts.Points(size=12, alpha=0.25, color='blue')
- rect_opts = opts.Rectangles(line_width=0, alpha=0.1, color='red')
- lon_lat = (0.5*sum(bbox[::2]), 0.5*sum(bbox[1::2]))
- return (gv.Points([lon_lat]) * gv.Rectangles([bbox])).opts(point_opts, rect_opts)
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-AOI = make_bbox(livingston_tx_lonlat, 0.5, 0.25)
-basemap = gv.tile_sources.OSM.opts(width=500, height=500)
-plot_bbox(AOI) * basemap
-```
-
-
-
-Agreguemos un intervalo de fechas. Las inundaciones ocurrieron principalmente entre el 30 de abril y el 2 de mayo. Estableceremos una ventana temporal más larga que cubra los meses de abril y mayo.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-start_date, stop_date = '2024-04-01', '2024-05-31'
-DATE_RANGE = f'{start_date}/{stop_date}'
-```
-
-
-
-Por último, se crea un un diccionario `search_params` que almacene el AOI y el intervalo de fechas. Este diccionario se utilizará para buscar datos en los STACs.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-search_params = dict(bbox=AOI, datetime=DATE_RANGE)
-print(search_params)
-```
-
-***
-
-## Obtención de los resultados de búsqueda
-
-### Ejecución de una búsqueda con la API PySTAC
-
-
-
-Para iniciar una búsqueda de datos se necesitan tres datos más: el _Endpoint_ (una URL), el _Proveedor_ (una cadena que representa una ruta que extiende el _Endpoint_) y los _Identificadores de la colección_ (una lista de cadenas que hacen referencia a catálogos específicos). Generalmente, debemos probar con el [sitio web de Earthdata Search](https://search.earthdata.nasa.gov) de la NASA para determinar correctamente los valores para los productos de datos específicos que queremos recuperar. El [repositorio de GitHub de la NASA CMR STAC también supervisa los problemas](https://github.com/nasa/cmr-stac/issues) relacionados con la API para las consultas de búsqueda de EarthData Cloud.
-
-Para la búsqueda de productos de datos DSWx que se quiere ejecutar, estos parámetros son los que se definen en la siguiente celda de código.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-ENDPOINT = 'https://cmr.earthdata.nasa.gov/stac' # base URL for the STAC to search
-PROVIDER = 'POCLOUD'
-COLLECTIONS = ["OPERA_L3_DSWX-HLS_V1_1.0"]
-# Update the dictionary opts with list of collections to search
-search_params.update(collections=COLLECTIONS)
-print(search_params)
-```
-
-
-
-Una vez que se definieron los parámetros de búsqueda en el diccionario de Python `search_params`, se puede instanciar un `Cliente` y buscar en el catálogo espacio-temporal de activos utilizando el método `Client.search`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-catalog = Client.open(f'{ENDPOINT}/{PROVIDER}/')
-search_results = catalog.search(**search_params)
-print(f'{type(search_results)=}\n',search_results)
-```
-
-
-
-El objeto `search_results` que se obtuvo al llamar al método `search` es del tipo `ItemSearch`. Para recuperar los resultados, invocamos al método `items` y convertimos el resultado en una `list` de Python que asociaremos al identificador `granules`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-%%time
-granules = list(search_results.items())
-print(f"Number of granules found with tiles overlapping given AOI: {len(granules)}")
-```
-
-
-
-Se analiza el contenido de la lista `granules`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-granule = granules[0]
-print(f'{type(granule)=}')
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-granule
-```
-
-
-
-El objeto `granule` tiene una representación de salida enriquecida en este cuaderno computacional de Jupyter. Podemos ampliar los atributos en la celda de salida haciendo clic en los triángulos.
-
-![](../assets/granule_output_repr.png)
-
-El término _grano_ se refiere a una colección de archivos de datos (datos ráster en este caso), todos ellos asociados a datos sin procesar adquiridos por un satélite concreto en una fecha y hora fija sobre un mosaico geográfico concreto. Hay una gran variedad de atributos interesantes asociados con este grano.
-
-- properties['datetime']: una cadena que representa la hora de adquisición de los datos de los archivos de datos ráster de este grano,
-- properties['eo:cloud_cover']: el porcentaje de píxeles oscurecidos por nubes y sombras de nubes en los archivos de datos ráster de este grano, y
-- `assets`: un `dict` de Python cuyos valores resumen las bandas o niveles de los datos ráster asociados con este gránulo.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-print(f"{type(granule.properties)=}\n")
-print(f"{granule.properties['datetime']=}\n")
-print(f"{granule.properties['eo:cloud_cover']=}\n")
-print(f"{type(granule.assets)=}\n")
-print(f"{granule.assets.keys()=}\n")
-```
-
-
-
-Cada objeto en `granule.assets` es una instancia de la clase `Asset` que tiene un atributo `href`. Es el atributo `href` el que nos indica dónde localizar un archivo GeoTiff asociado con el activo de este gránulo.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-for a in granule.assets:
- print(f"{a=}\t{type(granule.assets[a])=}")
- print(f"{granule.assets[a].href=}\n\n")
-```
-
-
-
-Además, el `Item` tiene un atributo `.id` que almacena una cadena de caracteres. Al igual que ocurre con los nombres de archivos asociados a los productos OPERA, esta cadena `.id` contiene el identificador de un mosaico geográfico MGRS. Podemos extraer ese identificador aplicando manipulación de cadenas con Python al atributo `.id` del gránulo. Se realiza y se almacena el resultado en la variable `tile_id`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-print(granule.id)
-tile_id = granule.id.split('_')[3]
-print(f"{tile_id=}")
-```
-
-***
-
-### Resumiendo los resultados de la búsqueda en un DataFrame
-
-
-
-Los detalles de los resultados de la búsqueda son complicados de analizar de esta manera. Se extraen algunos campos concretos de los gránulos obtenidos en un `DataFrame` de Pandas utilizando una función de Python. Definiremos la función aquí y la reutilizaremos en cuadernos posteriores.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-# utility to extract search results into a Pandas DataFrame
-def search_to_dataframe(search):
- '''Constructs Pandas DataFrame from PySTAC Earthdata search results.
- DataFrame columns are determined from search item properties and assets.
- 'asset': string identifying an Asset type associated with a granule
- 'href': data URL for file associated with the Asset in a given row.'''
- granules = list(search.items())
- assert granules, "Error: empty list of search results"
- props = list({prop for g in granules for prop in g.properties.keys()})
- tile_ids = map(lambda granule: granule.id.split('_')[3], granules)
- rows = (([g.properties.get(k, None) for k in props] + [a, g.assets[a].href, t])
- for g, t in zip(granules,tile_ids) for a in g.assets )
- df = pd.concat(map(lambda x: pd.DataFrame(x, index=props+['asset','href', 'tile_id']).T, rows),
- axis=0, ignore_index=True)
- assert len(df), "Empty DataFrame"
- return df
-```
-
-
-
-Invocar `search_to_dataframe` en `search_results` codifica la mayor parte de la información importante de la búsqueda como un Pandas `DataFrame`, tal como se muestra a continuación.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-df = search_to_dataframe(search_results)
-df.head()
-```
-
-
-
-El método `DataFrame.info` nos permite examinar la estructura de este DataFrame.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-df.info()
-```
-
-
-
-Se limpia el DataFrame que contiene los resultados de búsqueda. Esto podría estar incluido en una función, pero vale la pena saber cómo se hace esto con Pandas de manera interactiva.
-
-En primer lugar, para estos resultados, solo es necesaria una columna `Datetime`, se pueden eliminar las demás.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-df = df.drop(['start_datetime', 'end_datetime'], axis=1)
-df.info()
-```
-
-
-
-A continuación, se arregla el esquema del `DataFrame` `df` convirtiendo las columnas en tipos de datos sensibles. También será conveniente utilizar la marca de tiempo de la adquisición como índice del DataFrame. Se realiza utilizando el método `DataFrame.set_index`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-df['datetime'] = pd.DatetimeIndex(df['datetime'])
-df['eo:cloud_cover'] = df['eo:cloud_cover'].astype(np.float16)
-str_cols = ['asset', 'href', 'tile_id']
-for col in str_cols:
- df[col] = df[col].astype(pd.StringDtype())
-df = df.set_index('datetime').sort_index()
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-df.info()
-```
-
-
-
-Como resultado se obtiene un DataFrame con un esquema conciso que se puede utilizar para manipulaciones posteriores. Agrupar los resultados de la búsqueda STAC en un `DataFrame` de Pandas de forma razonable es un poco complicado. Varias de las manipulaciones anteriores podrían haberse incluido en la función `search_to_dataframe`. Pero, dado que los resultados de búsqueda de la API de STAC aún están evolucionando, actualmente es mejor ser flexible y utilizar Pandas de forma interactiva para trabajar con los resultados de búsqueda. Se verá esto con más detalle en ejemplos posteriores.
-
-
-
-***
-
-## Explorar y refinar los resultados de búsqueda
-
-
-
-Si se examina la columna numérica `eo:cloud_cover` del DataFrame `df`, se pueden recopilar estadísticas utilizando agregaciones estándar y el método `DataFrame.agg`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-df['eo:cloud_cover'].agg(['min','mean','median','max'])
-```
-
-
-
-Observa que hay varias entradas `nan` en esta columna. Las funciones de agregación estadística de Pandas suelen ser "`nan`-aware", esto significa que ignoran implícitamente las entradas `nan` al calcular las estadísticas.
-
-
-
-### Filtrado del DataFrame de búsqueda con Pandas
-
-
-
-Como primera operación de filtrado, se mantienen solo las filas para las que la cobertura de las nubes es inferior al 50%.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-df_clear = df.loc[df['eo:cloud_cover']<50]
-df_clear
-```
-
-
-
-Para esta consulta de búsqueda, cada gránulo DSWX comprende datos ráster para diez bandas o niveles. Se puede ver esto aplicando el método Pandas `Series.value_counts` a la columna `asset`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-df_clear.asset.value_counts()
-```
-
-
-
-Se filtran las filas que corresponden a la banda `B01_WTR` del producto de datos DSWx. La función de Pandas `DataFrame.str` hace que esta operación sea sencilla. Se nombra al `DataFrame` filtrado como `b01_wtr`.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-b01_wtr = df_clear.loc[df_clear.asset.str.contains('B01_WTR')]
-b01_wtr.info()
-b01_wtr.asset.value_counts()
-```
-
-
-
-También se puede observar que hay varios mosaicos geográficos asociados a los gránulos encontrados que intersecan el AOI proporcionado.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-b01_wtr.tile_id.value_counts()
-```
-
-
-
-Recuerda que estos códigos se refieren a mosaicos geográficos MGRS especificados en un sistema de coordenadas concreto. Como se identifican estos códigos en la columna `tile_id`, se puede filtrar las filas que corresponden, por ejemplo, a los archivos recopilados sobre el mosaico T15RUQ del MGRS:
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-b01_wtr_t15ruq = b01_wtr.loc[b01_wtr.tile_id=='T15RUQ']
-b01_wtr_t15ruq
-```
-
-
-
-Se obtiene un `DataFrame` `b01_wtr_t15ruq` mucho más corto que resume las ubicaciones remotas de los archivos (por ejemplo, GeoTiffs) que almacenan datos ráster para la banda de aguas superficiales `B01_WTR` en el mosaico MGRS `T15RUQ` recopilados en varias marcas de tiempo que se encuentran dentro de la ventana de tiempo que especificamos. Se puede utilizar este DataFrame para descargar esos archivos para su análisis o visualización.
-
-
-
-***
-
-## Procesamiento de datos para obtener resultados relevantes
-
-### Apilamiento de los datos
-
-
-
-Se cuenta con un `DataFrame` que identifica archivos remotos específicos de datos ráster. El siguiente paso es combinar estos datos ráster en una estructura de datos adecuada para el análisis. El Xarray `DataArray` es adecuado en este caso. La combinación puede generarse utilizando la función `concat` de Xarray. La función `urls_to_stack` en la siguiente celda es larga pero no es complicada. Toma un `DataFrame` con marcas de tiempo en el índice y una columna etiquetada `href` de las URL, lee los archivos asociados a esas URL uno a uno, y apila las matrices bidimensionales relevantes de datos ráster en una matriz tridimensional.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-def urls_to_stack(granule_dataframe):
- '''Processes DataFrame of PySTAC search results (with OPERA tile URLs) &
- returns stacked Xarray DataArray (dimensions time, latitude, & longitude)'''
-
- stack = []
- for i, row in granule_dataframe.iterrows():
- with rasterio.open(row.href) as ds:
- # extract CRS string
- crs = str(ds.crs).split(':')[-1]
- # extract the image spatial extent (xmin, ymin, xmax, ymax)
- xmin, ymin, xmax, ymax = ds.bounds
- # the x and y resolution of the image is available in image metadata
- x_res = np.abs(ds.transform[0])
- y_res = np.abs(ds.transform[4])
- # read the data
- img = ds.read()
- # Ensure img has three dimensions (bands, y, x)
- if img.ndim == 2:
- img = np.expand_dims(img, axis=0)
- lon = np.arange(xmin, xmax, x_res)
- lat = np.arange(ymax, ymin, -y_res)
- bands = np.arange(img.shape[0])
- da = xr.DataArray(
- data=img,
- dims=["band", "lat", "lon"],
- coords=dict(
- lon=(["lon"], lon),
- lat=(["lat"], lat),
- time=i,
- band=bands
- ),
- attrs=dict(
- description="OPERA DSWx B01",
- units=None,
- ),
- )
- da.rio.write_crs(crs, inplace=True)
- stack.append(da)
- return xr.concat(stack, dim='time').squeeze()
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-%%time
-stack = urls_to_stack(b01_wtr_t15ruq)
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-stack
-```
-
-### Crear una visualización de los datos
-
-```{code-cell} python jupyter={"source_hidden": false}
-# Define a colormap with RGBA tuples
-COLORS = [(150, 150, 150, 0.1)]*256 # Setting all values to gray with low opacity
-COLORS[0] = (0, 255, 0, 0.1) # Not-water class to green
-COLORS[1] = (0, 0, 255, 1) # Open surface water
-COLORS[2] = (0, 0, 255, 1) # Partial surface water
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-image_opts = dict(
- x='lon',
- y='lat',
- project=True,
- rasterize=True,
- cmap=COLORS,
- colorbar=False,
- tiles = gv.tile_sources.OSM,
- widget_location='bottom',
- frame_width=500,
- frame_height=500,
- xlabel='Longitude (degrees)',
- ylabel='Latitude (degrees)',
- title = 'DSWx data for May 2024 Texas floods',
- fontscale=1.25
- )
-```
-
-
-
-Visualizar las imágenes completas puede consumir mucha memoria. Se utiliza el método Xarray `DataArray.isel` para extraer un trozo del arreglo `stack` con menos píxeles. Esto permitirá un rápido renderizado y desplazamiento.
-
-
-
-```{code-cell} python jupyter={"source_hidden": false}
-view = stack.isel(lon=slice(3000,None), lat=slice(3000,None))
-view.hvplot.image(**image_opts)
-```
-
-```{code-cell} python jupyter={"source_hidden": false}
-stack.hvplot.image(**image_opts) # Construct view from all slices.
-```
-
-
-
-Antes de continuar, recuerda apagar el kernel de este cuaderno computacional para liberar memoria para otros cálculos.
-
-
-
-***
-
-
-
-Este cuaderno computacional proporciona principalmente un ejemplo para ilustrar el uso de la API de PySTAC.
-
-En los siguientes cuadernos computacionales, utilizaremos este flujo de trabajo general:
-
-1. Establecer una consulta de búsqueda mediante la identificación de un _AOI_ particular y un _intervalo de fechas_.
-2. Identificar un _endpoint_, un _proveedor_ y un _catálogo de activos_ adecuados, y ejecutar la búsqueda utilizando `pystac.Client`.
-3. Convertir los resultados de la búsqueda en un DataFrame de Pandas que contenga los principales campos de interés.
-4. Utilizar el DataFrame resultante para filtrar los archivos de datos remotos más relevantes necesarios para el análisis y/o la visualización.
-5. Ejecutar el análisis y/o la visualización utilizando el DataFrame para recuperar los datos requeridos.
-
-
diff --git a/book/es-419/04_Case_Studies/00_Template.md b/book/es-419/04_Case_Studies/00_Template.md
deleted file mode 100644
index 55ea976..0000000
--- a/book/es-419/04_Case_Studies/00_Template.md
+++ /dev/null
@@ -1,232 +0,0 @@
----
-jupyter:
- jupytext:
- text_representation:
- extension: .md
- format_name: markdown
- format_version: "1.3"
- jupytext_version: 1.16.2
- kernelspec:
- display_name: Python 3 (ipykernel)
- language: python
- name: python3
----
-
-# Plantilla para el uso de del servicio cloud ofrecido por EarthData
-
-## Esquema de los pasos para el análisis
-
-
-
-- Identificación de los parámetros de búsqueda
- - Área de interés (AOI, por las siglas en inglés de _area of interest_) y ventana temporal
- - _Endpoint_, proveedor, identificador del catálogo ("nombre corto")
-- Obtención de los resultados de la búsqueda
- - Exploracion, análisis para identificar características, bandas de interés
- - Almacenar los resultados en un DataFrame para facilitar la exploración
-- Explorar y refinar los resultados de la búsqueda
- - Identificar los gránulos de mayor valor
- - Filtrar los gránulos atípicos con mínima contribución
- - Combinar los gránulos filtrados relevantes en un DataFrame
- - Identificar el tipo de salida a generar
-- Procesar los datos para obtener resultados relevantes
- - Descargar los gránulos relevantes en Xarray DataArray, apilados adecuadamente
- - Realizar los cálculos intermedios necesarios
- - Combinar los datos relevantes en una visualización
-
-
-
-***
-
-### Importación preliminar de librerías
-
-```python jupyter={"source_hidden": false}
-from warnings import filterwarnings
-filterwarnings('ignore')
-# data wrangling imports
-import numpy as np
-import pandas as pd
-import xarray as xr
-import rioxarray as rio
-import rasterio
-```
-
-```python jupyter={"source_hidden": false}
-# Imports for plotting
-import hvplot.pandas
-import hvplot.xarray
-import geoviews as gv
-from geoviews import opts
-gv.extension('bokeh')
-```
-
-```python jupyter={"source_hidden": false}
-# STAC imports to retrieve cloud data
-from pystac_client import Client
-from osgeo import gdal
-# GDAL setup for accessing cloud data
-gdal.SetConfigOption('GDAL_HTTP_COOKIEFILE','~/.cookies.txt')
-gdal.SetConfigOption('GDAL_HTTP_COOKIEJAR', '~/.cookies.txt')
-gdal.SetConfigOption('GDAL_DISABLE_READDIR_ON_OPEN','EMPTY_DIR')
-gdal.SetConfigOption('CPL_VSIL_CURL_ALLOWED_EXTENSIONS','TIF, TIFF')
-```
-
-### Funciones prácticas
-
-Estas funciones podrían incluirse en archivos de módulos para proyectos de investigación más evolucionados. Para fines didácticos, se incluyen en este cuaderno computacional.
-
-```python jupyter={"source_hidden": false}
-# simple utility to make a rectangle with given center of width dx & height dy
-def make_bbox(pt,dx,dy):
- '''Returns bounding-box represented as tuple (x_lo, y_lo, x_hi, y_hi)
- given inputs pt=(x, y), width & height dx & dy respectively,
- where x_lo = x-dx/2, x_hi=x+dx/2, y_lo = y-dy/2, y_hi = y+dy/2.
- '''
- return tuple(coord+sgn*delta for sgn in (-1,+1) for coord,delta in zip(pt, (dx/2,dy/2)))
-```
-
-```python jupyter={"source_hidden": false}
-# simple utility to plot an AOI or bounding-box
-def plot_bbox(bbox):
- '''Given bounding-box, returns GeoViews plot of Rectangle & Point at center
- + bbox: bounding-box specified as (lon_min, lat_min, lon_max, lat_max)
- Assume longitude-latitude coordinates.
- '''
- # These plot options are fixed but can be over-ridden
- point_opts = opts.Points(size=12, alpha=0.25, color='blue')
- rect_opts = opts.Rectangles(line_width=0, alpha=0.1, color='red')
- lon_lat = (0.5*sum(bbox[::2]), 0.5*sum(bbox[1::2]))
- return (gv.Points([lon_lat]) * gv.Rectangles([bbox])).opts(point_opts, rect_opts)
-```
-
-```python jupyter={"source_hidden": false}
-# utility to extract search results into a Pandas DataFrame
-def search_to_dataframe(search):
- '''Constructs Pandas DataFrame from PySTAC Earthdata search results.
- DataFrame columns are determined from search item properties and assets.
- 'asset': string identifying an Asset type associated with a granule
- 'href': data URL for file associated with the Asset in a given row.'''
- granules = list(search.items())
- assert granules, "Error: empty list of search results"
- props = list({prop for g in granules for prop in g.properties.keys()})
- tile_ids = map(lambda granule: granule.id.split('_')[3], granules)
- rows = (([g.properties.get(k, None) for k in props] + [a, g.assets[a].href, t])
- for g, t in zip(granules,tile_ids) for a in g.assets )
- df = pd.concat(map(lambda x: pd.DataFrame(x, index=props+['asset','href', 'tile_id']).T, rows),
- axis=0, ignore_index=True)
- assert len(df), "Empty DataFrame"
- return df
-```
-
-```python jupyter={"source_hidden": false}
-# utility to process DataFrame of search results & return DataArray of stacked raster images
-def urls_to_stack(granule_dataframe):
- '''Processes DataFrame of PySTAC search results (with OPERA tile URLs) &
- returns stacked Xarray DataArray (dimensions time, latitude, & longitude)'''
-
- stack = []
- for i, row in granule_dataframe.iterrows():
- with rasterio.open(row.href) as ds:
- # extract CRS string
- crs = str(ds.crs).split(':')[-1]
- # extract the image spatial extent (xmin, ymin, xmax, ymax)
- xmin, ymin, xmax, ymax = ds.bounds
- # the x and y resolution of the image is available in image metadata
- x_res = np.abs(ds.transform[0])
- y_res = np.abs(ds.transform[4])
- # read the data
- img = ds.read()
- # Ensure img has three dimensions (bands, y, x)
- if img.ndim == 2:
- img = np.expand_dims(img, axis=0)
- lon = np.arange(xmin, xmax, x_res)
- lat = np.arange(ymax, ymin, -y_res)
- bands = np.arange(img.shape[0])
- da = xr.DataArray(
- data=img,
- dims=["band", "lat", "lon"],
- coords=dict(
- lon=(["lon"], lon),
- lat=(["lat"], lat),
- time=i,
- band=bands
- ),
- attrs=dict(
- description="OPERA DSWx B01",
- units=None,
- ),
- )
- da.rio.write_crs(crs, inplace=True)
- stack.append(da)
- return xr.concat(stack, dim='time').squeeze()
-```
-
-***
-
-## Identificación de los parámetros de búsqueda
-
-```python jupyter={"source_hidden": false}
-AOI = ...
-DATE_RANGE = ...
-```
-
-```python jupyter={"source_hidden": false}
-# Optionally plot the AOI
-```
-
-```python jupyter={"source_hidden": false}
-search_params = dict(bbox=AOI, datetime=DATE_RANGE)
-print(search_params)
-```
-
-***
-
-## Obtención de los resultados de la búsqueda
-
-```python jupyter={"source_hidden": false}
-ENDPOINT = ...
-PROVIDER = ...
-COLLECTIONS = ...
-# Update the dictionary opts with list of collections to search
-search_params.update(collections=COLLECTIONS)
-print(search_params)
-```
-
-```python jupyter={"source_hidden": false}
-catalog = Client.open(f'{ENDPOINT}/{PROVIDER}/')
-search_results = catalog.search(**search_params)
-```
-
-```python
-df = search_to_dataframe(search_results)
-df.head()
-```
-
-Limpiar el DataFrame `df` de forma que tenga sentido (por ejemplo, eliminando columnas/filas innecesarias, convirtiendo columnas en tipos de datos fijos, estableciendo un índice, etc.).
-
-```python
-```
-
-***
-
-## Exploración y refinamiento de los resultados de la búsqueda
-
-
-
-Consiste en filtrar filas o columnas adecuadamente para limitar los resultados de la búsqueda a los archivos de datos ráster más relevantes para el análisis y/o la visualización. Esto puede significar enfocarse en determinadas regiones geográficos, bandas específicas del producto de datos, determinadas fechas o períodos, etc.
-
-
-
-```python
-```
-
-***
-
-## Procesamiento de los datos para obtener resultados relevantes
-
-Esto puede incluir apilar matrices bidimensionales en una matriz tridimensional, combinar imágenes ráster de mosaicos adyacentes en uno solo, etc.
-
-```python
-```
-
-***
diff --git a/book/es-419/01_Geospatial_Background/02_Working_with_Raster_Data.md b/book/espanol/01_Geospatial_Background/02_Working_with_Raster_Data.md
similarity index 100%
rename from book/es-419/01_Geospatial_Background/02_Working_with_Raster_Data.md
rename to book/espanol/01_Geospatial_Background/02_Working_with_Raster_Data.md
diff --git a/book/es-419/01_Geospatial_Background/03_Working_with_Vector_Data.md b/book/espanol/01_Geospatial_Background/03_Working_with_Vector_Data.md
similarity index 100%
rename from book/es-419/01_Geospatial_Background/03_Working_with_Vector_Data.md
rename to book/espanol/01_Geospatial_Background/03_Working_with_Vector_Data.md
diff --git a/book/espanol/03_Using_NASA_EarthData/01_Using_OPERA_DIST_Products.md b/book/espanol/03_Using_NASA_EarthData/01_Using_OPERA_DIST_Products.md
index 4506caa..81569dc 100644
--- a/book/espanol/03_Using_NASA_EarthData/01_Using_OPERA_DIST_Products.md
+++ b/book/espanol/03_Using_NASA_EarthData/01_Using_OPERA_DIST_Products.md
@@ -5,7 +5,7 @@ jupyter:
extension: .md
format_name: markdown
format_version: "1.3"
- jupytext_version: 1.16.2
+ jupytext_version: 1.16.4
kernelspec:
display_name: Python 3 (ipykernel)
language: python
@@ -20,43 +20,43 @@ jupyter:
-
+
-Del proyecto (Observational Products for End-Users from Remote Sensing Analysis)](https://www.jpl.nasa.gov/go/opera) (Productos de observación para usuarios finales a partir del análisis por teledetección):
+Del proyecto [Observational Products for End-Users from Remote Sensing Analysis (OPERA)](https://www.jpl.nasa.gov/go/opera) (en español, Productos de Observación para Usuarios Finales a partir del Analisis por Teledetección):
-> Iniciado en abril del 2021, el proyecto OPERA (Observational Products for End-Users from Remote Sensing Analysis) (Productos de observación para usuarios finales a partir del análisis por teledetección) del Jet Propulsion Laboratory recopila datos de instrumentos ópticos y de radar por satélite para generar seis conjuntos de productos:
+> Iniciado en abril del 2021, el proyecto OPERA del _Jet Propulsion Laboratory_ (JPL) (en español, Laboratorio de Propulsión a Chorro) recopila datos satelitales ópticos y de radar para generar seis conjuntos de productos:
>
-> - un conjunto de productos sobre la extensión de las aguas superficiales a escala casi mundial
-> - un conjunto de productos de alteración de la superficie casi mundial
-> - un producto radiométrico corregido del terreno casi global
-> - un conjunto de productos complejos Coregistered Single Look de Norteamérica
-> - un paquete de productos de North America Displacement
-> - un conjunto de productos de North America Vertical Land Motion
+> - un conjunto de productos sobre la extensión de las aguas de la superficie terrestre a escala casi mundial
+> - un conjunto de productos de Alteraciones de la Superficie terrestre a escala casi mundial
+> - un producto con Corrección Radiométrica del Terreno a escala casi mundial
+> - un conjunto de productos _Coregistered Single Look Complex_ para Norteamérica
+> - un conjunto de productos de Desplazamiento para Norteamérica
+> - un conjunto de productos de Movimiento Vertical del Terreno en Norteamérica
-Es decir, OPERA es una iniciativa de la NASA que toma, por ejemplo, datos de teledetección óptica o radar recopilados desde satélites, y genera una variedad de conjuntos de datos preprocesados para uso público. Los productos de OPERA no son imágenes de satélite sin procesar, sino el resultado de una clasificación algorítmica para determinar, por ejemplo, qué regiones terrestres contienen agua o dónde se ha desplazado la vegetación. Las imágenes de satélite sin procesar se recopilan a partir de mediciones realizadas por los instrumentos a bordo de las misiones de los satélites Sentinel-1 A/B, Sentinel-2 A/B y Landsat-8/9 (de ahí el término _HLS_ para "_Harmonized Landsat-Sentinel_" en numerosas descripciones de productos).
+Es decir, OPERA es una iniciativa de la National Aeronautics and Space Administration (NASA, en español, Administración Nacional de Aeronáutica y del Espacio) que toma, por ejemplo, datos de teledetección óptica o radar recopilados desde satélites, y genera una variedad de conjuntos de datos preprocesados para uso público. Los productos de OPERA no son imágenes de satélite sin procesar, sino el resultado de una clasificación algorítmica para determinar, por ejemplo, qué regiones terrestres contienen agua o dónde se ha modificado la vegetación. Las imágenes de satélite sin procesar se recopilan a partir de mediciones realizadas por los instrumentos a bordo de las misiones de los satélites Sentinel-1 A/B, Sentinel-2 A/B y Landsat-8/9 (de ahí el término _Harmonized Landsat-Sentinel_" (HLS) (en español, Landsat-Sentinel Armonizadas) para en numerosas descripciones de productos).
----
+***
-## El producto OPERA Land Surface Disturbance (DIST)
+## El producto OPERA _Land Surface Disturbance_ (DIST) (en español, Perturbación de la superficie terrestre)
-
+
-Uno de estos productos de datos OPERA es el producto _Land Surface Disturbance (DIST)_ (descrito con más detalle en la [OPERA DIST HLS product specification](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)) (especificación del producto OPERA DIST HLS).
-El producto DIST cartografía la alteración de la vegetación (específicamente, la pérdida de cobertura vegetal por píxel HLS siempre que haya una disminución indicada) a partir de escenas del Harmonized Landsat-8 y el Sentinel-2 A/B (HLS). Una de las aplicaciones de estos datos es cuantificar los daños causados por los _incendios forestales_. El producto DIST_ALERT se publica a intervalos regulares (la misma cadencia de las imágenes HLS, aproximadamente cada 12 días en un determinado mosaico/región). El producto DIST_ANN resume las mediciones de las alteraciones a lo largo de un año.
+Uno de estos productos de datos de OPERA es el producto DIST (descrito con más detalle en la especificación del producto [OPERA DIST HLS](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)).
+Los productos DIST mapean la _perturbación de la vegetación_ (en concreto, la pérdida de cubierta vegetal por píxel HLS siempre que haya una disminución indicada) a partir de escenas armonizadas Landsat-8 y Sentinel-2 A/B (HLS). Una de las aplicaciones de estos datos es cuantificar los daños causados por los _incendios forestales_. El producto DIST_ALERT se publica a intervalos regulares (al igual que las imágenes HLS, aproximadamente cada 12 días en un determinado mosaico/región). El producto DIST_ANN resume las mediciones de las alteraciones a lo largo de un año.
-Los productos DIST cuantifican los datos de reflectancia de la superficie (SR) adquiridos a partir del Operational Land Imager (OLI) a bordo del satélite de teledetección Landsat-8 y del Multi-Spectral Instrument (MSI) a bordo del satélite de teledetección Sentinel-2 A/B. Los productos de datos HLS DIST son archivos de datos rasterizados, cada uno de ellos asociado a mosaicos de la superficie terrestre. Cada mosaico se representa mediante coordenadas cartográficas proyectadas alineadas con el [_Military Grid Reference System (MGRS)_](https://en.wikipedia.org/wiki/Military_Grid_Reference_System) (Sistema de Referencia de Cuadrículas Militares (MGRS)). Cada mosaico cubre 109.8 $km^2$ divididos en 3660 filas y 3660 columnas, con una separación de 30 metros entre píxeles, con mosaicos que se solapan con los vecinos en 4900 metros en cada dirección (los detalles se describen completamente en la [DIST product specification](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)) (especificación DIST del producto).
+Los productos DIST cuantifican los datos de reflectancia de la superficie (RS) (en inglés, Surface Reflectance, SR) adquiridos a partir de imágenes terrestres operacionales _Operational Land Imager_ (OLI) (en español, Generador de Imágenes Terrestres Operacional) a bordo del satélite de teledetección Landsat-8 y del _Multi-Spectral Instrument_ (MSI) (en español, Instrumento Multiespectral) a bordo del satélite de teledetección Sentinel-2 A/B. Los productos de datos HLS DIST son archivos de tipo ráster, cada uno de ellos asociado a mosaicos de la superficie terrestre. Cada mosaico se representa mediante coordenadas cartográficas proyectadas alineadas con el [Sistema de Referencia de Cuadrículas Militares (MGRS, por sus siglas en inglés de _Military Grid Reference System_)](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). Cada mosaico se divide en 3,660 filas y 3,660 columnas con un espaciado de píxeles de 30 metros (así que un mosaico es de $109.8\,\mathrm{km}$ largo en cada lado). Los mosaicos vecinos se solapan 4.900 metros en cada dirección (los detalles se describen detalladamente en la [especificación de producto DIST](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)).
-Los productos OPERA DIST se distribuyen como [Cloud Optimized GeoTIFFs](https://www.cogeo.org/) (GeoTIFF optimizados para la nube). En la práctica, eso significa que las diferentes bandas se almacenan en archivos TIFF distintos. La especificación TIFF permite el almacenamiento de matrices multidimensionales en un único archivo. El almacenamiento de bandas distintas en archivos TIFF distintos permite que los archivos se descarguen de forma independiente.
+Los productos OPERA DIST se distribuyen como [GeoTIFFs optimizados para la nube](https://www.cogeo.org/); en la práctica, esto significa que las diferentes bandas se almacenan en archivos de formato TIFFs (TIFF, por sus siglas en inglés, _Tagged Image File Format_) distintos. La especificación TIFF permite el almacenamiento de matrices multidimensionales en un único archivo. El almacenamiento de bandas distintas en diferentes archivos TIFF permite que estos se descarguen de forma independiente.
----
+***
@@ -64,108 +64,110 @@ Los productos OPERA DIST se distribuyen como [Cloud Optimized GeoTIFFs](https://
-
+
-Examinemos un archivo local con un ejemplo de datos DIST-ALERT. El archivo contiene la primera banda de datos de alteración: la _anomalía de pérdida máxima de vegetación_. Para cada píxel, se trata de un valor entre 0% y 100% que representa la diferencia porcentual entre la cobertura vegetal que se observa actualmente y un valor de referencia histórico. Es decir, un valor de 100 corresponde a una pérdida completa de vegetación en un píxel y un valor de 0 corresponde a ninguna pérdida de vegetación. Los valores de los píxeles se almacenan como enteros sin signo de 8 bits (UInt8) porque los valores de los píxeles solo deben oscilar entre 0 y 100. Un valor del píxel de 255 indica que faltan datos, es decir, que los datos HLS no pudieron destilar un valor máximo de anomalía en la vegetación para ese píxel. Por supuesto, el uso de datos enteros sin signo de 8 bits es mucho más eficiente para el almacenamiento y para la transmisión de datos a través de una red (en comparación con, por ejemplo, datos de punto flotante de 32 o 64 bits).
+Examina un archivo local con un ejemplo de datos DIST-ALERT. El archivo contiene la primera banda de datos de alteración: la _anomalía de pérdida máxima de vegetación_. Para cada píxel, se trata de un valor entre 0% y 100% que representa la diferencia porcentual entre la cobertura vegetal que se observa actualmente y un valor de referencia histórico. Es decir, un valor de 100 corresponde a una pérdida total de vegetación en un píxel y un valor de 0 corresponde a que no hubo pérdida de vegetación. Los valores de los píxeles se almacenan como enteros sin signo de 8 bits (UInt8) porque los valores de los píxeles solo deben oscilar entre 0 y 100. Un valor del píxel de 255 indica que faltan datos, es decir, que los datos HLS no pudieron determinar un valor máximo de anomalía en la vegetación para ese píxel. Por supuesto, el uso de datos enteros sin signo de 8 bits es mucho más eficiente para el almacenamiento y para la transmisión de datos a través de una red (en comparación con, por ejemplo, datos de punto flotante de 32 o 64 bits).
-Empecemos importando las bibliotecas necesarias. Observa que también estamos importando algunas clases de la biblioteca Bokeh para hacer que los gráficos interactivos sean un poco más atractivos.
+Empieza importando las librerías necesarias. Observa que también estamos importando la clase `FixedTicker` de la librería Bokeh para hacer que los gráficos interactivos sean un poco más atractivos.
-```python editable=true jupyter={"source_hidden": true} slideshow={"slide_type": ""}
+```{code-cell} python editable=true jupyter={"source_hidden": false} slideshow={"slide_type": ""}
# Notebook dependencies
import warnings
warnings.filterwarnings('ignore')
from pathlib import Path
import rioxarray as rio
-import hvplot.xarray
-from bokeh.models import FixedTicker
import geoviews as gv
gv.extension('bokeh')
+import hvplot.xarray
+from bokeh.models import FixedTicker
+
+FILE_STEM = Path.cwd().parent if 'book' == Path.cwd().parent.stem else 'book'
```
-
+
-Vamos a leer los datos de un archivo local `'OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-ANOM-MAX.tif'`. Antes de cargarlo, analicemos los metadatos incrustados en el nombre del archivo.
+Lee los datos de un archivo local `'OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-ANOM-MAX.tif'`. Antes de cargarlo, analiza los metadatos incluídos en el nombre del archivo.
-```python jupyter={"source_hidden": true}
-LOCAL_PATH = Path('..') / 'assets' / 'OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-ANOM-MAX.tif'
+```{code-cell} python jupyter={"source_hidden": false}
+LOCAL_PATH = Path(FILE_STEM, 'assets/OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-ANOM-MAX.tif')
filename = LOCAL_PATH.name
print(filename)
```
-
+
-Este nombre de archivo bastante largo incluye varios campos separados por caracteres de subrayado (`_`). Podemos utilizar el método `str.split` de Python para ver más fácilmente los distintos campos.
+Este nombre de archivo bastante largo incluye varios campos separados por caracteres de guión bajo (`_`). Podemos utilizar el método `str.split` de Python para ver más fácilmente los distintos campos.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
filename.split('_') # Use the Python str.split method to view the distinct fields more easily.
```
-
+
-Los archivos del producto OPERA tienen un esquema de nombres particular (tal como se describe en la [DIST product specification](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf) (especificación del producto DIST)). En la salida anterior, podemos extraer ciertos metadatos para este ejemplo:
+Los archivos de los productos OPERA tienen un esquema de nombres particular (como se describe en la [especificación de producto DIST](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)). En la salida anterior, puedes extraer ciertos metadatos para este ejemplo:
1. _Product_: `OPERA`;
2. _Level_: `L3` ;
3. _ProductType_: `DIST-ALERT-HLS` ;
-4. _TileID_: `T10TEM` (cadena que hace referencia a un mosaico del [Military Grid Reference System (MGRS)](https://en.wikipedia.org/wiki/Military_Grid_Reference_System)) (Sistema de Referencia de Cuadrículas Militares (MGRS));
+4. _TileID_: `T10TEM` (cadena de caracteres que hace referencia a un mosaico del [MGRS](https://en.wikipedia.org/wiki/Military_Grid_Reference_System));
5. _AcquisitionDateTime_: `20220815T185931Z` (cadena que representa una marca de tiempo GMT para la adquisición de los datos);
6. _ProductionDateTime_ : `20220817T153514Z` (cadena que representa una marca de tiempo GMT para cuando se generó el producto de los datos);
7. _Sensor_: `S2A` (identificador del satélite que adquirió los datos sin procesar: `L8` (Landsat-8), `S2A` (Sentinel-2 A) o `S2B` (Sentinel-2 B);
8. _Resolution_: `30` (por ejemplo, píxeles de longitud lateral $30\mathrm{m}$);
-9. _ProductVersion_: `v0.1`; y
+9. _ProductVersion_: `v0.1` (versión del producto); y
10. _LayerName_: `VEG-ANOM-MAX`
-Ten en cuenta que la NASA utiliza nomenclaturas convencionales como [Earthdata Search](https://search.earthdata.nasa.gov) para extraer datos significativos de los [SpatioTemporal Asset Catalogs (STAC)](https://stacspec.org/) ( Catálogos de Activos Espaciales y Temporales (STAC)). Más adelante utilizaremos estos campos, en particular los campos _TileID_ y _LayerName_, para filtrar los resultados de la búsqueda antes de recuperar los datos remotos.
+Ten en cuenta que la NASA utiliza nomenclaturas convencionales como [Earthdata Search](https://search.earthdata.nasa.gov) para extraer datos significativos de los [_SpatioTemporal Asset Catalogs_ (STACs)](https://stacspec.org/) (en español, Catálogos de Activos Espaciales y Temporales). Más adelante se utilizarán estos campo— en particular _TileID_ y _LayerName_; para filtrar los resultados de la búsqueda antes de recuperar los datos remotos.
-
+
-Vamos a subir los datos de este archivo local en un Xarray `DataArray` utilizando `rioxarray.open_rasterio`. Reetiquetaremos las coordenadas adecuadamente y extraeremos el CRS (sistema de referencia de coordenadas).
+Sube los datos de este archivo local en un `DataArray`, que es un tipo de dato de Xarray, utilizando `rioxarray.open_rasterio`. Reetiqueta las coordenadas adecuadamente y extrae el CRS (sistema de referencia de coordenadas).
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
data = rio.open_rasterio(LOCAL_PATH)
crs = data.rio.crs
-data = data.rename({'x':'easting', 'y':'northing', 'band':'band'}).squeeze()
+data = data.rename({'x':'longitude', 'y':'latitude', 'band':'band'}).squeeze()
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
data
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
crs
```
-
+
-Antes de generar un gráfico, vamos a crear un mapa base utilizando mosaicos [ESRI](https://en.wikipedia.org/wiki/Esri).
+Antes de generar un gráfico, crea un mapa base utilizando mosaicos [ESRI](https://es.wikipedia.org/wiki/Esri).
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
# Creates basemap
-base = gv.tile_sources.ESRI.opts(width=1000, height=1000, padding=0.1)
+base = gv.tile_sources.ESRI.opts(width=750, height=750, padding=0.1)
```
-
+
-También utilizaremos diccionarios para capturar la mayor parte de las opciones de trazado que utilizaremos más adelante junto con `.hvplot.image`.
+También utiliza diccionarios para capturar la mayor parte de las opciones de trazado que utilizarás más adelante junto con `.hvplot.image`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
image_opts = dict(
- x='easting',
- y='northing',
+ x='longitude',
+ y='latitude',
rasterize=True,
dynamic=True,
frame_width=500,
@@ -181,82 +183,82 @@ layout_opts = dict(
)
```
-
+
-Por último, utilizaremos el método `DataArray.where` para filtrar los píxeles que faltan y los píxeles que no han tenido cambios en la vegetación, y modificaremos las opciones en `image_opts` y `layout_opts` a valores particulares para este conjunto de datos.
+Por último, usa el método `DataArray.where` para filtrar los píxeles que faltan y los que no vieron ningún cambio en la vegetación; estos valores de píxeles serán reasignados como `nan` por lo que serán transparentes cuando el raster sea visualizado. También modifica ligeramente las opciones de `image_opts` y `layout_opts`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
veg_anom_max = data.where((data>0) & (data!=255))
image_opts.update(crs=data.rio.crs)
layout_opts.update(title=f"VEG_ANOM_MAX")
```
-
+
-Esto nos permitirá generar un gráfico significativo.
+Estos cambios permiten generar una visualización útil.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
veg_anom_max.hvplot.image(**image_opts).opts(**layout_opts) * base
```
-
+
En el gráfico resultante, los píxeles blancos y amarillos corresponden a regiones en las que se ha producido cierta deforestación, pero no mucha. Por el contrario, los píxeles oscuros y negros corresponden a regiones que han perdido casi toda la vegetación.
----
+***
## Banda 2: Fecha de alteración inicial de la vegetación (VEG_DIST_DATE)
-
+
-Los productos DIST-ALERT contienen varias bandas (tal como se resume en la [DIST product specification](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)) (especificación del producto DIST). La segunda banda que analizaremos es la _fecha de alteración inicial de la vegetación_ en el último año. Esta se almacena como un número entero de 16 bits (Int16).
+Los productos DIST-ALERT contienen varias bandas (tal como se resume en la [ especificación de productos DIST](https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf)). La segunda banda que se analiza es la _fecha de alteración inicial de la vegetación_ en el último año. Esta se almacena como un número entero de 16 bits (Int16).
-El archivo `OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-DATE.tif` se almacena localmente. La [DIST product specification](\(https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf\)) (especificación del producto DIST) describe cómo utilizar las convenciones para la denominación de archivos. Aquí destaca la _fecha y hora de adquisición_ `20220815T185931`, por ejemplo, casi las 7 p.m. (UTC) del 15 de agosto del 2022.
+El archivo `OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-DATE.tif` se almacena localmente. La [especificación de productos DIST](\(https://d2pn8kiwq2w21t.cloudfront.net/documents/OPERA_DIST_HLS_Product_Specification_V1.pdf\)) describe cómo utilizar las convenciones para la denominación de archivos. Aquí destaca la _fecha y hora de adquisición_ `20220815T185931`, por ejemplo, casi las 7 p.m. (UTC) del 15 de agosto del 2022.
-Cargaremos y reetiquetaremos el `DataArray` como antes.
+Cargar y reetiqueta el `DataArray` como antes.
-```python jupyter={"source_hidden": true}
-LOCAL_PATH = Path('..') / 'assets' / 'OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-DATE.tif'
+```{code-cell} python jupyter={"source_hidden": false}
+LOCAL_PATH = Path(FILE_STEM, 'assets/OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-DATE.tif')
data = rio.open_rasterio(LOCAL_PATH)
-data = data.rename({'x':'easting', 'y':'northing', 'band':'band'}).squeeze()
+data = data.rename({'x':'longitude', 'y':'latitude', 'band':'band'}).squeeze()
```
-
+
-En esta banda en particular, el valor 0 indica que no ha habido alteraciones en el último año y -1 es un valor centinela que indica que faltan datos. Cualquier valor positivo es el número de días desde el 31 de diciembre del 2020 en los que se midió la primera alteración en ese píxel. Filtraremos los valores no positivos y conservaremos estos valores significativos utilizando `DataArray.where`.
+En esta banda en particular, el valor 0 indica que no ha habido alteraciones en el último año y -1 es un valor que indica que faltan datos. Cualquier valor positivo es el número de días desde el 31 de diciembre del 2020 en los que se midió la primera alteración en ese píxel. Filtrar los valores no positivos y conserva estos valores significativos utilizando `DataArray.where`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
veg_dist_date = data.where(data>0)
```
-
+
-Vamos a examinar el rango de valores numéricos en `veg_dist_date` utilizando DataArray.min`and`DataArray.max`. Ambos métodos ignorarán los píxeles que contengan `nan\` ("Not-a-Number") al calcular el mínimo y el máximo.
+Examina el rango de valores numéricos en `veg_dist_date` utilizando DataArray.min`and`DataArray.max`. Ambos métodos ignorarán los píxeles que contengan `nan\` (por sus siglas en inglés de _Not-a-Number_) al calcular el mínimo y el máximo.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
d_min, d_max = int(veg_dist_date.min().item()), int(veg_dist_date.max().item())
print(f'{d_min=}\t{d_max=}')
```
-
+
-En este caso, los datos significativos se encuentran entre 247 y 592. Recuerda que se trata del número de días transcurridos desde el 31 de diciembre del 2020, cuando se observó la primera alteración en el último año. Dado que estos datos se adquirieron el 15 de agosto del 2022, los únicos valores posibles estarían entre 227 y 592 días. Así que debemos recalibrar el mapa de colores en el gráfico
+En este caso, los datos relevantes se encuentran entre 247 y 592. Recuerda que se trata del número de días transcurridos desde el 31 de diciembre del 2020, cuando se observó la primera alteración en el último año. Dado que estos datos se adquirieron el 15 de agosto del 2022, los únicos valores posibles estarían entre 227 y 592 días. Así que debes recalibrar los colores en la visualización
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
image_opts.update(
clim=(d_min,d_max),
crs=data.rio.crs
@@ -264,17 +266,17 @@ image_opts.update(
layout_opts.update(title=f"VEG_DIST_DATE")
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
veg_dist_date.hvplot.image(**image_opts).opts(**layout_opts) * base
```
-
+
Con este mapa de colores, los píxeles más claros mostraron algunos signos de deforestación hace cerca de un año. Por el contrario, los píxeles negros mostraron deforestación por primera vez cerca del momento de adquisición de los datos. Por tanto, esta banda es útil para seguir el avance de los incendios forestales a medida que arrasan los bosques.
----
+***
@@ -282,42 +284,45 @@ Con este mapa de colores, los píxeles más claros mostraron algunos signos de d
-
+
-Por último, veamos una tercera banda de la familia de productos DIST-ALERT denominada _estado de alteración de la vegetación_. Estos valores de píxel se almacenan como enteros de 8 bits sin signo. Solo hay 6 valores distintos almacenados:
+Por último, se analiza una tercera banda de la familia de productos DIST-ALERT denominada _estado de alteración de la vegetación_. Estos valores de píxel se almacenan como enteros de 8 bits sin signo. Solo hay 6 valores distintos almacenados:
- **0:** Sin alteración
- **1:** Alteración provisional (**primera detección**) con cambio en la cubierta vegetal < 50%
- **2:** Alteración confirmada (**detección recurrente**) con cambio en la cubierta vegetal < 50%
-- **3:** Alteración provisional con cambio en la cubierta vegetal ≥ 50%
-- **4:** Alteración confirmada con cambio en la cubierta vegetal ≥ 50%
+- **3:** Alteración provisional con cambio en la cobertura vegetal ≥ 50%
+- **4:** Alteración confirmada con cambio en la cobertura vegetal ≥ 50%
- **255**: Datos no disponibles
El valor de un píxel se marca como cambiado provisionalmente cuando la pérdida de la cobertura vegetal (alteración) es observada por primera vez por un satélite. Si el cambio se vuelve a notar en posteriores adquisiciones HLS sobre dicho píxel, entonces el píxel se marca como confirmado.
-
+
-Podemos usar un archivo local como ejemplo de esta capa/banda particular de los datos DIST-ALERT. El código es el mismo que el anterior, pero observa que:
+Se puede usar un archivo local como ejemplo de esta capa/banda particular de los datos DIST-ALERT. El código es el mismo que el anterior, pero observa que:
-- los datos filtrados reflejan los valores de píxel significativos para esta capa (por ejemplo, `data<0` and `data<5`), y
-- los límites del mapa de colores se reasignan en consecuencia (es decir, de 0 a 4).
+- los datos filtrados reflejan los valores de píxel significativos para esta capa (por ejemplo, `data>0` and `data<5`), y
+- los valores del mapa de colores se reasignan en consecuencia (es decir, de 0 a 4).
+-
+
+Observa el uso de `FixedTicker` en la definición de una barra de colores más adecuada para un mapa de color discreto (es decir, categórico).
-```python jupyter={"source_hidden": true}
-LOCAL_PATH = Path('..') / 'assets' / 'OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-STATUS.tif'
+```{code-cell} python jupyter={"source_hidden": false}
+LOCAL_PATH = Path(FILE_STEM, 'assets/OPERA_L3_DIST-ALERT-HLS_T10TEM_20220815T185931Z_20220817T153514Z_S2A_30_v0.1_VEG-DIST-STATUS.tif')
data = rio.open_rasterio(LOCAL_PATH)
-data = data.rename({'x':'easting', 'y':'northing', 'band':'band'}).squeeze()
+data = data.rename({'x':'longitude', 'y':'latitude', 'band':'band'}).squeeze()
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
veg_dist_status = data.where((data>0)&(data<5))
image_opts.update(crs=data.rio.crs)
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
layout_opts.update(
title=f"VEG_DIST_STATUS",
clim=(0,4),
@@ -325,14 +330,14 @@ layout_opts.update(
)
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
veg_dist_status.hvplot.image(**image_opts).opts(**layout_opts) * base
```
-
+
-Este mapa de colores continuo no resalta especialmente bien las características de este gráfico. Una mejor opción sería un mapa de colores _categórico_. Veremos cómo conseguirlo en el próximo cuaderno (con los productos de datos OPERA DSWx).
+Este mapa de colores continuo no resalta correctamente las características de este gráfico. Una mejor opción sería un mapa de colores _categórico_. Se mostrará como hacerlo en el próximo cuaderno computacional (con los productos de datos OPERA DSWx).
----
+***
diff --git a/book/espanol/03_Using_NASA_EarthData/03_Using_PySTAC.md b/book/espanol/03_Using_NASA_EarthData/03_Using_PySTAC.md
index e2b5bb6..8cf249a 100644
--- a/book/espanol/03_Using_NASA_EarthData/03_Using_PySTAC.md
+++ b/book/espanol/03_Using_NASA_EarthData/03_Using_PySTAC.md
@@ -12,55 +12,55 @@ jupyter:
name: python3
---
-# Uso de la API PySTAC
+# Uso de la API de PySTAC
-
+
-En el [Earthdata Search website](https://search.earthdata.nasa.gov) (sitio web Earthdata Search) de la NASA se puede buscar gran cantidad de datos. El enlace anterior se conecta a una interfaz gráfica de usuario para buscar en [SpatioTemporal Asset Catalogs (STACs)](https://stacspec.org/) (catálogos de activos espaciotemporales (STAC)) al especificar una área de interés (AOI) y una _ventana temporal_ o un _intervalo de fechas_.
+En el sitio web [Earthdata Search](https://search.earthdata.nasa.gov) de la NASA se puede buscar una gran cantidad de datos. El enlace anterior se conecta a una interfaz gráfica de usuario (GUI, por sus siglas en inglés de _Graphical User Interface_) para buscar en los [catálogos activos espaciotemporales (STACs, por sus siglas en inglés de _SpatioTemporal Asset Catalogs_)](https://stacspec.org/) al especificar un área de interés (AOI, por sus siglas en inglés de _Area of Interest_) y una _ventana temporal_ o un _intervalo de fechas_.
-Por motivos de reproducibilidad, queremos ser capaces de buscar en los catálogos de activos mediante programación. Aquí es donde entra en juego la biblioteca [PySTAC](https://pystac.readthedocs.io/en/stable/).
+En pos de la reproducibilidad, se busca que las personas usuarias sean capaces de buscar en los catálogos de activos de manera programática. Aquí es donde entra en juego la librería [PySTAC](https://pystac.readthedocs.io/en/stable/).
----
+***
## Esquema de las etapas del análisis
-
+
- Identificación de los parámetros de búsqueda
- AOI, ventana temporal
- Endpoint, proveedor, identificador del catálogo ("nombre corto")
- Obtención de los resultados de la búsqueda
- - Instrospección, análisis para identificar características, bandas de interés
- - Envolver los resultados en un DataFrame para facilitar la exploración
+ - Exploración de datos, análisis para identificar características, bandas de interés
+ - Almacenar los resultados en un DataFrame para facilitar la exploración
- Explorar y refinar los resultados de la búsqueda
- - Identificar los gránulos de mayor valor
- - Filtrar los gránulos extraños con una contribución mínima
- - Reunir los gránulos filtrados correspondientes en un DataFrame
- - Identificar el tipo de resultado que se quiere generar
+ - Identificar los granos de mayor valor
+ - Filtrar los granos anómalos con una contribución mínima
+ - Combinar los granos filtrados correspondientes en un DataFrame
+ - Identificar el tipo de salida que se quiere obtener
- Procesamiento de los datos para generar resultados relevantes
- - Descargar los gránulos relevantes en Xarray DataArray, apilados adecuadamente
- - Llevar a cabo los cálculos intermedios necesarios
- - Ensamblar los fragmentos de datos relevantes en la visualización
+ - Descargar los granos relevantes en un tipo de dato DataArray de la libreria Xarray, apilados adecuadamente
+ - Realizar los cálculos intermedios necesarios
+ - Integrar los fragmentos de datos relevantes en una visualización
----
+***
## Identificar los parámetros de búsqueda
### Definir el AOI y el rango de fechas
-
+
-Empezaremos tomando en cuenta un ejemplo concreto. [Heavy rains severely impacted Southeast Texas in May 2024](https://www.texastribune.org/2024/05/03/texas-floods-weather-harris-county/) (Fuertes lluvias afectaron gravemente al sureste de Texas en mayo del 2024), provocando [flooding and causing significant damage to property and human life](https://www.texastribune.org/series/east-texas-floods-2024/) (inundaciones y causando importantes daños materiales y humanos).
+Comenzaremos tomando en cuenta un ejemplo concreto. [Las fuertes lluvias afectaron gravemente al sureste de Texas en mayo de 2024](https://www.texastribune.org/2024/05/03/texas-floods-weather-harris-county/), provocando [inundaciones y causando importantes daños materiales y humanos](https://www.texastribune.org/series/east-texas-floods-2024/).
-Como es usual, se requieren ciertas importaciones relevantes. Las dos primeras celdas son familiares (relacionadas con las herramientas de análisis y la visualización de los datos que ya se examinaron). La tercera celda incluye importaciones de la biblioteca `pystac_client` y de la biblioteca `gdal`, seguidas de algunos ajustes necesarios para utilizar la [GDAL (the Geospatial Data Abstraction Library)](https://gdal.org) (Biblioteca de Abstracción de Datos Geoespaciales). Estos detalles en la configuración permiten que las sesiones de tu cuaderno interactúen sin problemas con las fuentes remotas de datos geoespaciales.
+Como es usual, se requiere la importación de ciertas librerías relevantes. Las dos primeras celdas son familiares (relacionadas con las herramientas de análisis y la visualización de los datos que ya se examinaron). La tercera celda incluye la importación de la biblioteca `pystac_client` y de la biblioteca `gdal`, seguidas de algunos ajustes necesarios para utilizar la [Biblioteca de Abstracción de Datos Geoespaciales (GDAL, por sus siglas en inglés, Geospatial Data Abstraction Library)](https://gdal.org). Estos detalles en la configuración permiten que las sesiones de tu cuaderno computacional interactúen sin problemas con las fuentes remotas de datos geoespaciales.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
from warnings import filterwarnings
filterwarnings('ignore')
# data wrangling imports
@@ -71,7 +71,7 @@ import rioxarray as rio
import rasterio
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
# Imports for plotting
import hvplot.pandas
import hvplot.xarray
@@ -80,7 +80,7 @@ from geoviews import opts
gv.extension('bokeh')
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
# STAC imports to retrieve cloud data
from pystac_client import Client
from osgeo import gdal
@@ -91,9 +91,9 @@ gdal.SetConfigOption('GDAL_DISABLE_READDIR_ON_OPEN','EMPTY_DIR')
gdal.SetConfigOption('CPL_VSIL_CURL_ALLOWED_EXTENSIONS','TIF, TIFF')
```
-
+
-A continuación, definiremos los parámetros de búsqueda geográfica para poder recuperar los datos correspondientes a ese evento de inundación. Esto consiste en especificar un _área de interés (AOI)_ y un _intervalo de fechas_.
+A continuación, definiremos los parámetros de búsqueda geográfica para poder recuperar los datos correspondientes a ese evento de inundación. Esto consiste en especificar un _AOI_ y un _intervalo de fechas_.
- El AOI se especifica como un rectángulo de coordenadas de longitud-latitud en una única 4-tupla con la forma
$$({\mathtt{longitude}}_{\mathrm{min}},{\mathtt{latitude}}_{\mathrm{min}},{\mathtt{longitude}}_{\mathrm{max}},{\mathtt{latitude}}_{\mathrm{max}}),$$
@@ -104,18 +104,18 @@ A continuación, definiremos los parámetros de búsqueda geográfica para poder
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
# Center of the AOI
livingston_tx_lonlat = (-95.09,30.69) # (lon, lat) form
```
-
+
-Escribiremos algunas funciones cortas para encapsular la lógica de nuestros flujos de trabajo genéricos. Para el código de investigación, estas se colocarían en archivos de módulos Python. Por conveniencia, incrustaremos las funciones en este cuaderno y otras para que puedan ejecutarse correctamente con dependencias mínimas.
+Escribiremos algunas funciones cortas para encapsular la lógica de nuestros flujos de trabajo genéricos. Para el código de investigación, estas se colocarían en archivos de Python. Por practicidad, incrustaremos las funciones en este cuaderno y en otros para que puedan ejecutarse correctamente con dependencias mínimas.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
# simple utility to make a rectangle with given center of width dx & height dy
def make_bbox(pt,dx,dy):
'''Returns bounding-box represented as tuple (x_lo, y_lo, x_hi, y_hi)
@@ -125,7 +125,7 @@ def make_bbox(pt,dx,dy):
return tuple(coord+sgn*delta for sgn in (-1,+1) for coord,delta in zip(pt, (dx/2,dy/2)))
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
# simple utility to plot an AOI or bounding-box
def plot_bbox(bbox):
'''Given bounding-box, returns GeoViews plot of Rectangle & Point at center
@@ -139,49 +139,49 @@ def plot_bbox(bbox):
return (gv.Points([lon_lat]) * gv.Rectangles([bbox])).opts(point_opts, rect_opts)
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
AOI = make_bbox(livingston_tx_lonlat, 0.5, 0.25)
basemap = gv.tile_sources.OSM.opts(width=500, height=500)
plot_bbox(AOI) * basemap
```
-
+
Agreguemos un intervalo de fechas. Las inundaciones ocurrieron principalmente entre el 30 de abril y el 2 de mayo. Estableceremos una ventana temporal más larga que cubra los meses de abril y mayo.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
start_date, stop_date = '2024-04-01', '2024-05-31'
DATE_RANGE = f'{start_date}/{stop_date}'
```
-
+
-Por último, crearemos un un diccionario `search_params` que almacene el AOI y el intervalo de fechas. Este diccionario se utilizará para buscar datos en los STAC.
+Por último, se crea un un diccionario `search_params` que almacene el AOI y el intervalo de fechas. Este diccionario se utilizará para buscar datos en los STACs.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
search_params = dict(bbox=AOI, datetime=DATE_RANGE)
print(search_params)
```
----
+***
## Obtención de los resultados de búsqueda
### Ejecución de una búsqueda con la API PySTAC
-
+
-Para iniciar una búsqueda de datos se necesitan tres datos más: el _Endpoint_ (una URL), el _Proveedor_ (una cadena que representa una ruta que extiende el Endpoint) y los _Identificadores de la colección_ (una lista de cadenas que hacen referencia a catálogos específicos). Generalmente, debemos experimentar con [Earthdata Search website](https://search.earthdata.nasa.gov) (sitio web Earthdata Search) de la NASA para determinar correctamente estos valores para los productos de los datos específicos que queremos recuperar. El [NASA CMR STAC GitHub repository also monitors issues](https://github.com/nasa/cmr-stac/issues) (repositorio de GitHub de la NASA CMR STAC también supervisa los problemas) relacionados con la API para las consultas de búsqueda de EarthData Cloud.
+Para iniciar una búsqueda de datos se necesitan tres datos más: el _Endpoint_ (una URL), el _Proveedor_ (una cadena que representa una ruta que extiende el _Endpoint_) y los _Identificadores de la colección_ (una lista de cadenas que hacen referencia a catálogos específicos). Generalmente, debemos probar con el [sitio web de Earthdata Search](https://search.earthdata.nasa.gov) de la NASA para determinar correctamente los valores para los productos de datos específicos que queremos recuperar. El [repositorio de GitHub de la NASA CMR STAC también supervisa los problemas](https://github.com/nasa/cmr-stac/issues) relacionados con la API para las consultas de búsqueda de EarthData Cloud.
-Para la búsqueda de productos de datos DSWx que queremos ejecutar, estos parámetros son los que se definen en la siguiente celda de código.
+Para la búsqueda de productos de datos DSWx que se quiere ejecutar, estos parámetros son los que se definen en la siguiente celda de código.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
ENDPOINT = 'https://cmr.earthdata.nasa.gov/stac' # base URL for the STAC to search
PROVIDER = 'POCLOUD'
COLLECTIONS = ["OPERA_L3_DSWX-HLS_V1_1.0"]
@@ -190,60 +190,60 @@ search_params.update(collections=COLLECTIONS)
print(search_params)
```
-
+
-Una vez que se definieron los parámetros de búsqueda en el diccionario de Python `search_params`, podemos instanciar un `Cliente` y buscar en el catálogo espacio-temporal de activos utilizando el método `Client.search`.
+Una vez que se definieron los parámetros de búsqueda en el diccionario de Python `search_params`, se puede instanciar un `Cliente` y buscar en el catálogo espacio-temporal de activos utilizando el método `Client.search`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
catalog = Client.open(f'{ENDPOINT}/{PROVIDER}/')
search_results = catalog.search(**search_params)
print(f'{type(search_results)=}\n',search_results)
```
-
+
-El objeto `search_results` que se obtuvo al llamar al método `search` es del tipo `ItemSearch`. Para recuperar los resultados, invocamos el método `items` y lanzamos el resultado como una `list` de Python que enlazaremos con los gránulos identificadores.
+El objeto `search_results` que se obtuvo al llamar al método `search` es del tipo `ItemSearch`. Para recuperar los resultados, invocamos al método `items` y convertimos el resultado en una `list` de Python que asociaremos al identificador `granules`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
%%time
granules = list(search_results.items())
print(f"Number of granules found with tiles overlapping given AOI: {len(granules)}")
```
-
+
-Analicemos el contenido de la lista `granules`.
+Se analiza el contenido de la lista `granules`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
granule = granules[0]
print(f'{type(granule)=}')
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
granule
```
-
+
-El objeto `granule` tiene una representación de salida enriquecida en este cuaderno de Jupyter. Podemos ampliar los atributos en la celda de salida haciendo clic en los triángulos.
+El objeto `granule` tiene una representación de salida enriquecida en este cuaderno computacional de Jupyter. Podemos ampliar los atributos en la celda de salida haciendo clic en los triángulos.
![](../assets/granule_output_repr.png)
-El término _gránulo_ se refiere a una colección de archivos de datos (datos ráster en este caso), todos ellos asociados a datos sin procesar adquiridos por un satélite concreto en una fecha y hora fijas sobre un mosaico geográfico concreto. Hay una gran variedad de atributos interesantes asociados con este gránulo.
+El término _grano_ se refiere a una colección de archivos de datos (datos ráster en este caso), todos ellos asociados a datos sin procesar adquiridos por un satélite concreto en una fecha y hora fija sobre un mosaico geográfico concreto. Hay una gran variedad de atributos interesantes asociados con este grano.
-- properties['datetime']: una cadena que representa la hora de adquisición de los datos de los archivos de datos ráster de este gránulo,
-- properties['eo:cloud_cover']: el porcentaje de píxeles oscurecidos por nubes y sombras de nubes en los archivos de datos ráster de este gránulo, y
+- properties['datetime']: una cadena que representa la hora de adquisición de los datos de los archivos de datos ráster de este grano,
+- properties['eo:cloud_cover']: el porcentaje de píxeles oscurecidos por nubes y sombras de nubes en los archivos de datos ráster de este grano, y
- `assets`: un `dict` de Python cuyos valores resumen las bandas o niveles de los datos ráster asociados con este gránulo.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
print(f"{type(granule.properties)=}\n")
print(f"{granule.properties['datetime']=}\n")
print(f"{granule.properties['eo:cloud_cover']=}\n")
@@ -251,41 +251,41 @@ print(f"{type(granule.assets)=}\n")
print(f"{granule.assets.keys()=}\n")
```
-
+
-Cada objeto en `granule.assets` es una instancia de la clase `Asset` que tiene un atributo `href`. Es el atributo `href` el que nos indica dónde localizar un archivo GeoTiff asociado con este activo de este gránulo.
+Cada objeto en `granule.assets` es una instancia de la clase `Asset` que tiene un atributo `href`. Es el atributo `href` el que nos indica dónde localizar un archivo GeoTiff asociado con el activo de este gránulo.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
for a in granule.assets:
print(f"{a=}\t{type(granule.assets[a])=}")
print(f"{granule.assets[a].href=}\n\n")
```
-
+
-Además, el `Item` tiene un atributo `.id` que almacena una cadena. Al igual que ocurre con los nombres de archivo asociados a los productos OPERA, esta cadena `.id` contiene el identificador de un mosaico geográfico MGRS. Podemos extraer ese identificador aplicando manipulaciones de cadenas de Python al atributo `.id` del gránulo. Hagámoslo y almacenemos el resultado en `tile_id`.
+Además, el `Item` tiene un atributo `.id` que almacena una cadena de caracteres. Al igual que ocurre con los nombres de archivos asociados a los productos OPERA, esta cadena `.id` contiene el identificador de un mosaico geográfico MGRS. Podemos extraer ese identificador aplicando manipulación de cadenas con Python al atributo `.id` del gránulo. Se realiza y se almacena el resultado en la variable `tile_id`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
print(granule.id)
tile_id = granule.id.split('_')[3]
print(f"{tile_id=}")
```
----
+***
### Resumiendo los resultados de la búsqueda en un DataFrame
-
+
-Los detalles de los resultados de la búsqueda son complicados de analizar de esta manera. Vamos a extraer algunos campos concretos de los gránulos obtenidos en un Pandas `DataFrame` utilizando una cómoda función de Python. Definiremos la función aquí y la reutilizaremos en cuadernos posteriores.
+Los detalles de los resultados de la búsqueda son complicados de analizar de esta manera. Se extraen algunos campos concretos de los gránulos obtenidos en un `DataFrame` de Pandas utilizando una función de Python. Definiremos la función aquí y la reutilizaremos en cuadernos posteriores.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
# utility to extract search results into a Pandas DataFrame
def search_to_dataframe(search):
'''Constructs Pandas DataFrame from PySTAC Earthdata search results.
@@ -304,47 +304,47 @@ def search_to_dataframe(search):
return df
```
-
+
Invocar `search_to_dataframe` en `search_results` codifica la mayor parte de la información importante de la búsqueda como un Pandas `DataFrame`, tal como se muestra a continuación.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
df = search_to_dataframe(search_results)
df.head()
```
-
+
-El método `DataFrame.info` nos permite examinar el esquema de este DataFrame.
+El método `DataFrame.info` nos permite examinar la estructura de este DataFrame.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
df.info()
```
-
+
-Vamos a limpiar el DataFrame de resultados de búsqueda. Esto podría estar incrustado en una función, pero, vale la pena saber cómo se hace esto interactivamente con Pandas.
+Se limpia el DataFrame que contiene los resultados de búsqueda. Esto podría estar incluido en una función, pero vale la pena saber cómo se hace esto con Pandas de manera interactiva.
-En primer lugar, para estos resultados, solo es necesaria una columna `Datetime`, podemos eliminar las demás.
+En primer lugar, para estos resultados, solo es necesaria una columna `Datetime`, se pueden eliminar las demás.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
df = df.drop(['start_datetime', 'end_datetime'], axis=1)
df.info()
```
-
+
-A continuación, arreglaremos el esquema del `DataFrame` `df` convirtiendo las columnas en tipos de datos sensibles. También será conveniente utilizar la marca de tiempo de la adquisición como índice del DataFrame. Hagámoslo utilizando el método `DataFrame.set_index`.
+A continuación, se arregla el esquema del `DataFrame` `df` convirtiendo las columnas en tipos de datos sensibles. También será conveniente utilizar la marca de tiempo de la adquisición como índice del DataFrame. Se realiza utilizando el método `DataFrame.set_index`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
df['datetime'] = pd.DatetimeIndex(df['datetime'])
df['eo:cloud_cover'] = df['eo:cloud_cover'].astype(np.float16)
str_cols = ['asset', 'href', 'tile_id']
@@ -353,111 +353,111 @@ for col in str_cols:
df = df.set_index('datetime').sort_index()
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
df.info()
```
-
+
-Al final esto da un DataFrame con un esquema conciso que puede utilizarse para manipulaciones posteriores. Agrupar los resultados de la búsqueda STAC en un Pandas `DataFrame` de forma sensata es un poco complicado. Varias de las manipulaciones anteriores podrían haberse incluido en la función `search_to_dataframe`. Pero, dado que los resultados de búsqueda de la API STAC aún están evolucionando, actualmente es mejor ser flexible y utilizar Pandas de forma interactiva para trabajar con los resultados de búsqueda. Veremos esto con más detalle en ejemplos posteriores.
+Como resultado se obtiene un DataFrame con un esquema conciso que se puede utilizar para manipulaciones posteriores. Agrupar los resultados de la búsqueda STAC en un `DataFrame` de Pandas de forma razonable es un poco complicado. Varias de las manipulaciones anteriores podrían haberse incluido en la función `search_to_dataframe`. Pero, dado que los resultados de búsqueda de la API de STAC aún están evolucionando, actualmente es mejor ser flexible y utilizar Pandas de forma interactiva para trabajar con los resultados de búsqueda. Se verá esto con más detalle en ejemplos posteriores.
----
+***
## Explorar y refinar los resultados de búsqueda
-
+
-Si examinamos la columna numérica `eo:cloud_cover` del DataFrame `df`, podemos recopilar estadísticas utilizando agregaciones estándar y el método `DataFrame.agg`.
+Si se examina la columna numérica `eo:cloud_cover` del DataFrame `df`, se pueden recopilar estadísticas utilizando agregaciones estándar y el método `DataFrame.agg`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
df['eo:cloud_cover'].agg(['min','mean','median','max'])
```
-
+
-Observa que hay varias entradas `nan` en esta columna. Las funciones de agregación estadística de Pandads suelen ser "`nan`-aware", esto significa que ignoran implícitamente las entradas `nan` ("Not-a-Number") al calcular las estadísticas.
+Observa que hay varias entradas `nan` en esta columna. Las funciones de agregación estadística de Pandas suelen ser "`nan`-aware", esto significa que ignoran implícitamente las entradas `nan` al calcular las estadísticas.
### Filtrado del DataFrame de búsqueda con Pandas
-
+
-Como primera operación de filtrado, vamos a mantener solo las filas para las que la cobertura en la nube es inferior al 50%.
+Como primera operación de filtrado, se mantienen solo las filas para las que la cobertura de las nubes es inferior al 50%.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
df_clear = df.loc[df['eo:cloud_cover']<50]
df_clear
```
-
+
-Para esta consulta de búsqueda, cada gránulo DSWX comprende datos ráster para diez bandas o niveles. Podemos ver esto aplicando el método Pandas `Series.value_counts` a la columna `asset`.
+Para esta consulta de búsqueda, cada gránulo DSWX comprende datos ráster para diez bandas o niveles. Se puede ver esto aplicando el método Pandas `Series.value_counts` a la columna `asset`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
df_clear.asset.value_counts()
```
-
+
-Vamos a filtrar las filas que corresponden a la banda `B01_WTR` del producto de datos DSWx. El accesorio de Pandas `DataFrame.str` hace que esta operación sea sencilla. Llamaremos al `DataFrame` filtrado `b01_wtr`.
+Se filtran las filas que corresponden a la banda `B01_WTR` del producto de datos DSWx. La función de Pandas `DataFrame.str` hace que esta operación sea sencilla. Se nombra al `DataFrame` filtrado como `b01_wtr`.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
b01_wtr = df_clear.loc[df_clear.asset.str.contains('B01_WTR')]
b01_wtr.info()
b01_wtr.asset.value_counts()
```
-
+
-También podemos ver que hay varios mosaicos geográficos asociados a los gránulos encontrados que intersecan el AOI proporcionado.
+También se puede observar que hay varios mosaicos geográficos asociados a los gránulos encontrados que intersecan el AOI proporcionado.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
b01_wtr.tile_id.value_counts()
```
-
+
-Recuerda que estos códigos se refieren a mosaicos geográficos MGRS especificados en un sistema de coordenadas concreto. Como identificamos estos códigos en la columna `tile_id`, podemos filtrar las filas que corresponden, por ejemplo, a los archivos recopilados sobre el mosaico T15RUQ del MGRS:
+Recuerda que estos códigos se refieren a mosaicos geográficos MGRS especificados en un sistema de coordenadas concreto. Como se identifican estos códigos en la columna `tile_id`, se puede filtrar las filas que corresponden, por ejemplo, a los archivos recopilados sobre el mosaico T15RUQ del MGRS:
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
b01_wtr_t15ruq = b01_wtr.loc[b01_wtr.tile_id=='T15RUQ']
b01_wtr_t15ruq
```
-
+
-Ahora tenemos un `DataFrame` `b01_wtr_t15ruq` mucho más corto que resume las ubicaciones remotas de los archivos (por ejemplo, GeoTiffs) que almacenan datos ráster para la banda de aguas superficiales `B01_WTR` en el mosaico MGRS `T15RUQ` recopilados en varias marcas de tiempo que se encuentran dentro de la ventana de tiempo que especificamos. Podemos utilizar este DataFrame para descargar esos archivos para su análisis o visualización.
+Se obtiene un `DataFrame` `b01_wtr_t15ruq` mucho más corto que resume las ubicaciones remotas de los archivos (por ejemplo, GeoTiffs) que almacenan datos ráster para la banda de aguas superficiales `B01_WTR` en el mosaico MGRS `T15RUQ` recopilados en varias marcas de tiempo que se encuentran dentro de la ventana de tiempo que especificamos. Se puede utilizar este DataFrame para descargar esos archivos para su análisis o visualización.
----
+***
## Procesamiento de datos para obtener resultados relevantes
### Apilamiento de los datos
-
+
-Tenemos un `DataFrame` que identifica archivos remotos específicos de datos ráster. El siguiente paso es combinar estos datos ráster en una estructura de datos adecuada para el análisis. El Xarray `DataArray` es adecuado en este caso. La combinación puede generarse utilizando la función `concat` de Xarray. La función `urls_to_stack` en la siguiente celda es larga pero no complicada. Toma un `DataFrame` con marcas de tiempo en el índice y una columna etiquetada `href` de las URL, lee los archivos asociados a esas URL uno a uno, y apila las matrices bidimensionales relevantes de datos ráster en una matriz tridimensional.
+Se cuenta con un `DataFrame` que identifica archivos remotos específicos de datos ráster. El siguiente paso es combinar estos datos ráster en una estructura de datos adecuada para el análisis. El Xarray `DataArray` es adecuado en este caso. La combinación puede generarse utilizando la función `concat` de Xarray. La función `urls_to_stack` en la siguiente celda es larga pero no es complicada. Toma un `DataFrame` con marcas de tiempo en el índice y una columna etiquetada `href` de las URL, lee los archivos asociados a esas URL uno a uno, y apila las matrices bidimensionales relevantes de datos ráster en una matriz tridimensional.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
def urls_to_stack(granule_dataframe):
'''Processes DataFrame of PySTAC search results (with OPERA tile URLs) &
returns stacked Xarray DataArray (dimensions time, latitude, & longitude)'''
@@ -499,18 +499,18 @@ def urls_to_stack(granule_dataframe):
return xr.concat(stack, dim='time').squeeze()
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
%%time
stack = urls_to_stack(b01_wtr_t15ruq)
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
stack
```
-### Producir una visualización de los datos
+### Crear una visualización de los datos
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
# Define a colormap with RGBA tuples
COLORS = [(150, 150, 150, 0.1)]*256 # Setting all values to gray with low opacity
COLORS[0] = (0, 255, 0, 0.1) # Not-water class to green
@@ -518,7 +518,7 @@ COLORS[1] = (0, 0, 255, 1) # Open surface water
COLORS[2] = (0, 0, 255, 1) # Partial surface water
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
image_opts = dict(
x='lon',
y='lat',
@@ -537,38 +537,38 @@ image_opts = dict(
)
```
-
+
-Trazar las imágenes totalmente puede consumir mucha memoria. Vamos a utilizar el método Xarray `DataArray.isel` para extraer un trozo del arreglo `stack` con menos píxeles. Esto permitirá un renderizado y desplazamiento rápidos.
+Visualizar las imágenes completas puede consumir mucha memoria. Se utiliza el método Xarray `DataArray.isel` para extraer un trozo del arreglo `stack` con menos píxeles. Esto permitirá un rápido renderizado y desplazamiento.
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
view = stack.isel(lon=slice(3000,None), lat=slice(3000,None))
view.hvplot.image(**image_opts)
```
-```python jupyter={"source_hidden": true}
+```{code-cell} python jupyter={"source_hidden": false}
stack.hvplot.image(**image_opts) # Construct view from all slices.
```
-
+
-Antes de continuar, recuerda apagar el kernel de este cuaderno para liberar memoria para otros cálculos.
+Antes de continuar, recuerda apagar el kernel de este cuaderno computacional para liberar memoria para otros cálculos.
----
+***
-
+
-Este cuaderno proporciona principalmente un ejemplo para ilustrar el uso de la API PySTAC.
+Este cuaderno computacional proporciona principalmente un ejemplo para ilustrar el uso de la API de PySTAC.
-En cuadernos posteriores, utilizaremos este flujo de trabajo general:
+En los siguientes cuadernos computacionales, utilizaremos este flujo de trabajo general:
1. Establecer una consulta de búsqueda mediante la identificación de un _AOI_ particular y un _intervalo de fechas_.
2. Identificar un _endpoint_, un _proveedor_ y un _catálogo de activos_ adecuados, y ejecutar la búsqueda utilizando `pystac.Client`.
-3. Convertir los resultados de la búsqueda en un Pandas DataFrame que contenga los principales campos de interés.
+3. Convertir los resultados de la búsqueda en un DataFrame de Pandas que contenga los principales campos de interés.
4. Utilizar el DataFrame resultante para filtrar los archivos de datos remotos más relevantes necesarios para el análisis y/o la visualización.
5. Ejecutar el análisis y/o la visualización utilizando el DataFrame para recuperar los datos requeridos.
diff --git a/book/espanol/04_Case_Studies/00_Template.md b/book/espanol/04_Case_Studies/00_Template.md
index 7534e0c..55ea976 100644
--- a/book/espanol/04_Case_Studies/00_Template.md
+++ b/book/espanol/04_Case_Studies/00_Template.md
@@ -12,35 +12,35 @@ jupyter:
name: python3
---
-# Plantilla para el uso de la nube EarthData
+# Plantilla para el uso de del servicio cloud ofrecido por EarthData
## Esquema de los pasos para el análisis
-
+
- Identificación de los parámetros de búsqueda
- - AOI, ventana temporal
- - Endpoint, proveedor, identificador del catálogo ("nombre corto")
+ - Área de interés (AOI, por las siglas en inglés de _area of interest_) y ventana temporal
+ - _Endpoint_, proveedor, identificador del catálogo ("nombre corto")
- Obtención de los resultados de la búsqueda
- - Instrospección, análisis para identificar características, bandas de interés
- - Envolver los resultados en un DataFrame para facilitar la exploración
+ - Exploracion, análisis para identificar características, bandas de interés
+ - Almacenar los resultados en un DataFrame para facilitar la exploración
- Explorar y refinar los resultados de la búsqueda
- Identificar los gránulos de mayor valor
- - Filtrar los gránulos extraños con una contribución mínima
- - Reunir los gránulos filtrados relevantes en un DataFrame
- - Identificar el tipo de resultado que se quiere generar
-- Procesamiento de los datos para generar resultados relevantes
+ - Filtrar los gránulos atípicos con mínima contribución
+ - Combinar los gránulos filtrados relevantes en un DataFrame
+ - Identificar el tipo de salida a generar
+- Procesar los datos para obtener resultados relevantes
- Descargar los gránulos relevantes en Xarray DataArray, apilados adecuadamente
- - Llevar a cabo los cálculos intermedios necesarios
- - Ensamblar los fragmentos de datos relevantes en la visualización
+ - Realizar los cálculos intermedios necesarios
+ - Combinar los datos relevantes en una visualización
----
+***
-### Importaciones preliminares
+### Importación preliminar de librerías
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
from warnings import filterwarnings
filterwarnings('ignore')
# data wrangling imports
@@ -51,7 +51,7 @@ import rioxarray as rio
import rasterio
```
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
# Imports for plotting
import hvplot.pandas
import hvplot.xarray
@@ -60,7 +60,7 @@ from geoviews import opts
gv.extension('bokeh')
```
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
# STAC imports to retrieve cloud data
from pystac_client import Client
from osgeo import gdal
@@ -71,11 +71,11 @@ gdal.SetConfigOption('GDAL_DISABLE_READDIR_ON_OPEN','EMPTY_DIR')
gdal.SetConfigOption('CPL_VSIL_CURL_ALLOWED_EXTENSIONS','TIF, TIFF')
```
-### Utilidades prácticas
+### Funciones prácticas
-Estas funciones podrían colocarse en archivos de módulos para proyectos de investigación más desarrollados. Para fines didácticos, se incluyen en este cuaderno.
+Estas funciones podrían incluirse en archivos de módulos para proyectos de investigación más evolucionados. Para fines didácticos, se incluyen en este cuaderno computacional.
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
# simple utility to make a rectangle with given center of width dx & height dy
def make_bbox(pt,dx,dy):
'''Returns bounding-box represented as tuple (x_lo, y_lo, x_hi, y_hi)
@@ -85,7 +85,7 @@ def make_bbox(pt,dx,dy):
return tuple(coord+sgn*delta for sgn in (-1,+1) for coord,delta in zip(pt, (dx/2,dy/2)))
```
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
# simple utility to plot an AOI or bounding-box
def plot_bbox(bbox):
'''Given bounding-box, returns GeoViews plot of Rectangle & Point at center
@@ -99,7 +99,7 @@ def plot_bbox(bbox):
return (gv.Points([lon_lat]) * gv.Rectangles([bbox])).opts(point_opts, rect_opts)
```
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
# utility to extract search results into a Pandas DataFrame
def search_to_dataframe(search):
'''Constructs Pandas DataFrame from PySTAC Earthdata search results.
@@ -118,7 +118,7 @@ def search_to_dataframe(search):
return df
```
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
# utility to process DataFrame of search results & return DataArray of stacked raster images
def urls_to_stack(granule_dataframe):
'''Processes DataFrame of PySTAC search results (with OPERA tile URLs) &
@@ -161,29 +161,29 @@ def urls_to_stack(granule_dataframe):
return xr.concat(stack, dim='time').squeeze()
```
----
+***
## Identificación de los parámetros de búsqueda
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
AOI = ...
DATE_RANGE = ...
```
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
# Optionally plot the AOI
```
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
search_params = dict(bbox=AOI, datetime=DATE_RANGE)
print(search_params)
```
----
+***
## Obtención de los resultados de la búsqueda
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
ENDPOINT = ...
PROVIDER = ...
COLLECTIONS = ...
@@ -192,7 +192,7 @@ search_params.update(collections=COLLECTIONS)
print(search_params)
```
-```python jupyter={"source_hidden": true}
+```python jupyter={"source_hidden": false}
catalog = Client.open(f'{ENDPOINT}/{PROVIDER}/')
search_results = catalog.search(**search_params)
```
@@ -202,31 +202,31 @@ df = search_to_dataframe(search_results)
df.head()
```
-Limpiar el DataFrame `df` de forma que tenga sentido (por ejemplo, eliminando columnas/filas innecesarias, convirtiendo columnas en tipos de datos fijos, estableciendo el índice, etc.).
+Limpiar el DataFrame `df` de forma que tenga sentido (por ejemplo, eliminando columnas/filas innecesarias, convirtiendo columnas en tipos de datos fijos, estableciendo un índice, etc.).
```python
```
----
+***
-## Explorar y refinar los resultados de la búsqueda
+## Exploración y refinamiento de los resultados de la búsqueda
-
+
-Consiste en filtrar filas o columnas adecuadamente para limitar los resultados de la búsqueda a los archivos de datos ráster más adecuados para el análisis y/o la visualización. Esto puede significar enfocarse en determinados mosaicos geográficos, bandas específicas del producto de datos, determinadas fechas/marcas de tiempo, etc.
+Consiste en filtrar filas o columnas adecuadamente para limitar los resultados de la búsqueda a los archivos de datos ráster más relevantes para el análisis y/o la visualización. Esto puede significar enfocarse en determinadas regiones geográficos, bandas específicas del producto de datos, determinadas fechas o períodos, etc.
```python
```
----
+***
-## Procesamiento de los datos para generar resultados relevantes
+## Procesamiento de los datos para obtener resultados relevantes
-Esto puede incluir apilar matrices bidimensionales en una matriz tridimensional, mosaico de imágenes ráster de mosaicos adyacentes en un solo mosaico, etc.
+Esto puede incluir apilar matrices bidimensionales en una matriz tridimensional, combinar imágenes ráster de mosaicos adyacentes en uno solo, etc.
```python
```
----
+***