Acceso a datos de temperatura de superficie terrestre de ECOSTRESS de NASA Earthdata en la nube#

La misión ECOsystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) mide la temperatura de las plantas para comprender mejor cuánta agua necesitan y cómo responden al estrés. ECOSTRESS está instalado en la Estación Espacial Internacional (ISS) y recolecta datos globalmente entre las latitudes 52° N y 52° S. Fuente: NASA Earthdata.
Requisitos#
Autenticación EDL (usuario/contraseña)
python>=3.12
Objetivos#
Descarga un archivo remoto con (sub)selection#
a) Por variables
b) Por coordenadas geograficas
Descarga de múltiples archivos remotos#
A cada archivo se le aplica la subseleccion de una subregion de datos de interes antes de descargar.
Referencias#
Hook, S., & Hulley, G.(2022). ECOSTRESS Swath Land Surface Temperature and Emissivity Instantaneous L2 Global 70 m v002 [Data set]. NASA Land Processes Distributed Active Archive Center. https://doi.org/10.5067/ECOSTRESS/ECO_L2_LSTE.002
import xarray as xr
import datetime as dt
import earthaccess
import pydap
import matplotlib.pyplot as plt
import numpy as np
# import pydap-specific tools
from pydap.client import get_cmr_urls, open_url
from pydap.client import to_netcdf as dap_to_netcdf
from pydap.net import create_session
Autenticación EDL mediante earthaccess y OPeNDAP#
Puedes autenticarte mediante earthaccess como se muestra a continuacion. Debes tener una cuenta EDL válida. Hay dos estrategias para autenticarte con earthaccess:
strategy="interactive". Esto pedirá tu usuario y contraseña de EDL.strategy="netrc". Usa esta opción si el notebook se ejecuta en un entorno donde se puede recuperar un.netrccon tus credenciales.
A continuación, el valor predeterminado será netrc, asumiendo que el usuario ejecutó el notebook Authenticate.ipynb. Si no es así, puedes cambiar la estrategia a "interactive".
from earthaccess.exceptions import LoginStrategyUnavailable
try:
auth = earthaccess.login(strategy="netrc", persist=True) # you will be promted to add your EDL credentials
except LoginStrategyUnavailable:
auth = earthaccess.login(strategy="interactive", persist=True)
# pass Token Authorization to a new Session.
my_session = create_session(auth.get_session())
Como encontrar URLs de OPeNDAP#
Usando la API CMR de NASA#
Consultamos el Common Metadata Repository (CMR por sus siglas en ingles) de la NASA para identificar archivos remotos que intersectan la siguiente área geográfica (caja delimitadora) y cubren el siguiente rango temporal:
-128.85 < longitud < -107.05, y 41.1 < latitud < 46.68
03/01 - 04/30 (2025)
ECOSTRESS_ccid = "C2076114664-LPCLOUD"
bounding_box = [-128.847656, 41.112469, -107.050781, 46.679594]
time_range = [dt.datetime(2025, 3, 1), dt.datetime(2025, 3, 30)]
cmr_urls = get_cmr_urls(ccid=ECOSTRESS_ccid, bounding_box = bounding_box, limit=1000, time_range=time_range) # limit by default = 50
print("################################################ \n We found a total of ", len(cmr_urls), "OPeNDAP URLS!!!\n################################################")
################################################
We found a total of 188 OPeNDAP URLS!!!
################################################
Acceso SOLO a metadatos con PyDAP#
Podemos acceder a metadatos producidos por OPeNDAP para identificar las variables de interés. En particular, aquellas asociadas con valores de latitud y longitud.
A continuación necesitamos solicitar los metadatos DAP4 al servidor remoto.
%%time
pyds = open_url(cmr_urls[0], protocol="dap4", session=my_session)
pyds.tree()
.ECOv002_L2_LSTE_37709_001_20250301T092419_0713_01.h5
├──L2 LSTE Metadata
│ ├──AncillaryNWP
│ ├──BandSpecification
│ ├──CloudMaxTemperature
│ ├──CloudMeanTemperature
│ ├──CloudMinTemperature
│ ├──CloudSDevTemperature
│ ├──Emis1GoodAvg
│ ├──Emis2GoodAvg
│ ├──Emis3GoodAvg
│ ├──Emis4GoodAvg
│ ├──Emis5GoodAvg
│ ├──LSTGoodAvg
│ ├──NWPSource
│ ├──NumberOfBands
│ ├──OrbitCorrectionPerformed
│ ├──QAPercentCloudCover
│ └──QAPercentGoodQuality
├──SDS
│ ├──Emis1
│ ├──Emis1_err
│ ├──Emis2
│ ├──Emis2_err
│ ├──Emis3
│ ├──Emis3_err
│ ├──Emis4
│ ├──Emis4_err
│ ├──Emis5
│ ├──Emis5_err
│ ├──EmisWB
│ ├──LST
│ ├──LST_err
│ ├──PWV
│ ├──QC
│ ├──cloud_mask
│ └──water_mask
└──StandardMetadata
├──AncillaryInputPointer
├──AutomaticQualityFlag
├──AutomaticQualityFlagExplanation
├──BuildID
├──CampaignShortName
├──CollectionLabel
├──DataFormatType
├──DayNightFlag
├──EastBoundingCoordinate
├──FieldOfViewObstruction
├──HDFVersionID
├──ImageLineSpacing
├──ImageLines
├──ImagePixelSpacing
├──ImagePixels
├──InputPointer
├──InstrumentShortName
├──LocalGranuleID
├──LongName
├──NorthBoundingCoordinate
├──PGEName
├──PGEVersion
├──PlatformLongName
├──PlatformShortName
├──PlatformType
├──ProcessingEnvironment
├──ProcessingLevelDescription
├──ProcessingLevelID
├──ProducerAgency
├──ProducerInstitution
├──ProductionDateTime
├──ProductionLocation
├──RangeBeginningDate
├──RangeBeginningTime
├──RangeEndingDate
├──RangeEndingTime
├──RegionID
├──SISName
├──SISVersion
├──SceneID
├──ShortName
├──SouthBoundingCoordinate
├──StartOrbitNumber
├──StopOrbitNumber
└──WestBoundingCoordinate
CPU times: user 167 ms, sys: 21 ms, total: 188 ms
Wall time: 1.79 s
Subdividir datos#
Primero, queremos subdividir datos con base en los criterios:
Datos diurnos
QAPercentCloudCover <30%
y QAPercentGoodQuality> 70%
Para eso, necesitamos descargar solo 3 variables:
["/L2 LSTE Metadata/QAPercentCloudCover",
"/L2 LSTE Metadata/QAPercentGoodQuality",
"/StandardMetadata/DayNightFlag"]
output_path = 'data/'
Descarga de datos al directorio local#
Descargaremos cada archivo remoto y los almacenaremos como un archivo individual, ya que estos no se pueden agregar.
%%time
dap_to_netcdf(cmr_urls, session=my_session, output_path = output_path,
keep_variables=["/L2 LSTE Metadata/QAPercentCloudCover",
"/L2 LSTE Metadata/QAPercentGoodQuality",
"/StandardMetadata/DayNightFlag"]
)
CPU times: user 170 ms, sys: 259 ms, total: 428 ms
Wall time: 12.4 s
Inspeccionar todos los archivos descargados#
Los datos ya se descargaron, y podemos filtrar más para identificar gránulos remotos por
Datos diurnos
QAPercentCloudCover <30%
y QAPercentGoodQuality> 70%
Luego actualizaremos nuestra lista de URLs desde las cuales descargar, con base en los archivos remotos que satisfacen los criterios anteriores.
NOTA: ¡Los archivos no se pueden agregar!
final_urls = []
for i in range(len(cmr_urls)):
local_file = output_path+cmr_urls[i].split("/")[-1]+".nc4"
dst = xr.open_datatree(local_file).load()
if dst['L2 LSTE Metadata/QAPercentCloudCover'].values < 30 and dst["L2 LSTE Metadata/QAPercentGoodQuality"] > 70 and dst["/StandardMetadata/DayNightFlag"] == 'Day':
final_urls.append(cmr_urls[i])
print("Total remote granules to download: ", len(final_urls))
Total remote granules to download: 15
Declara solo las variables de interes por descargar#
En este punto, es crucial eliminar cualquier dato ECOSTRESS descargado en el directorio data_output. Para eso abre una terminal y navega al directorio data.
import os
import glob
fnames = [output_path+f"{fname.split('/')[-1]}.nc4" for fname in cmr_urls]
for filename in fnames:
try:
os.remove(filename)
except FileNotFoundError:
print(f"The file '{filename}' is not in there anymore")
# define all variables of interest that our final download will have
keep_vars = ["/StandardMetadata/EastBoundingCoordinate",
"/StandardMetadata/SouthBoundingCoordinate",
"/StandardMetadata/NorthBoundingCoordinate",
"/StandardMetadata/WestBoundingCoordinate",
"/StandardMetadata/DayNightFlag",
"/StandardMetadata/ImagePixels",
"/StandardMetadata/ImagePixelSpacing",
"/StandardMetadata/ImageLines",
"/StandardMetadata/RangeBeginningDate",
"/StandardMetadata/RangeBeginningTime",
"/StandardMetadata/RangeEndingDate",
"/L2 LSTE Metadata/QAPercentCloudCover",
"/L2 LSTE Metadata/QAPercentGoodQuality",
"/SDS/QC", "/SDS/LST",
]
Descarga de datos#
No olvides usar ahora la lista final_urls, que apunta a nuestros gránulos de interés.
%%time
dap_to_netcdf(final_urls, session=my_session, output_path = output_path,
keep_variables=keep_vars,
)
CPU times: user 38.1 ms, sys: 132 ms, total: 170 ms
Wall time: 6.79 s
Inspeccionar archivos locales#
Los datos ya se descargaron.
NOTA: estos datasets no se pueden agregar.
local_file = output_path+final_urls[0].split("/")[-1]+".nc4"
dst = xr.open_datatree(local_file)
dst
<xarray.DataTree>
Group: /
├── Group: /SDS
│ Dimensions: (dim1: 5632, dim2: 5400)
│ Dimensions without coordinates: dim1, dim2
│ Data variables:
│ LST (dim1, dim2) float64 243MB ...
│ QC (dim1, dim2) uint16 61MB ...
├── Group: /L2 LSTE Metadata
│ Dimensions: (dim0: 1)
│ Dimensions without coordinates: dim0
│ Data variables:
│ QAPercentCloudCover (dim0) int32 4B ...
│ QAPercentGoodQuality (dim0) int32 4B ...
└── Group: /StandardMetadata
Dimensions: (dim3: 1)
Dimensions without coordinates: dim3
Data variables:
DayNightFlag <U3 12B ...
EastBoundingCoordinate (dim3) float64 8B ...
ImageLines (dim3) int32 4B ...
ImagePixelSpacing (dim3) float64 8B ...
ImagePixels (dim3) int32 4B ...
NorthBoundingCoordinate (dim3) float64 8B ...
RangeBeginningDate <U10 40B ...
RangeBeginningTime <U15 60B ...
RangeEndingDate <U10 40B ...
SouthBoundingCoordinate (dim3) float64 8B ...
WestBoundingCoordinate (dim3) float64 8B ...(dst["SDS/LST"]-273.15).plot(vmin=-9, vmax=5, cmap='nipy_spectral')
<matplotlib.collections.QuadMesh at 0x169c00380>