•  Icon
  •  Icon
  •  Icon
  •  Icon

Access Land Surface Temperature from NASA’s Ecostress

Access Land Surface Temperature from NASA’s Ecostress
Subset-driven, parallel access to Land Surface Temperature data from Ecostress via OPeNDAP and Pydap, enabling efficient remote workflows
Geospatial data icon About the Data
The dataset accessed in this tutorial is freely available, and the ECOsystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) mission measures the temperature of plants to better understand how much water plants need and how they respond to stress. ECOSTRESS is attached to the International Space Station (ISS) and collects data globally between 52Β° N and 52Β° S latitudes. Source: NASA Earthdata.

Requirements

  • Earthdata login (EDL) credentials.
  • Concept Collection ID or DOI for the relevant data product.
  • Python >= 3.11.
  • Mamba-forge (or conda-forge) installed on the machine.
  • Familiarity with Jupyter notebooks and Jupyter Lab.

Optional:

  • Store all EDL credentials in a .netrc file.
  • Basic knowledge of conda environment installation.

Objectives

To download one month of Land Surface Temperature data from an Ecostress data Collection. The spatial and temporal range is defined by the following parameters:

  • Time range: 03/01/2025 – 04/30/2025.
  • Spatial range: -128.8 < longitude < -107.05, and 41.1 < latitude < 46.6.

To accomplish this goal above, the tutorial will demonstrate how to:

  • Authenticate (via earthaccess).
  • Search for all available NASA OPeNDAP URLs for a specific NASA collection. The search will further filter by time range.
  • Subset with OPeNDAP, by variable name and spatial / temporal range.

Install required python dependencies

In a terminal shell, use mamba or conda forge to install all required dependencies to run this tutorial and activate the environment to run an interactive jupyter notebook on a browser.

Terminal
$ mamba create -n opendap_env -c conda-forge python=3.12 ipython jupyterlab earthaccess netCDF4
$ mamba activate opendap_env
$ pip install git+https://github.com/pydap/pydap.git
$ jupyter lab

Once in the jupyter notebook environment, import in the first cell all necessary methods that will be used to stream remote data into a local file:

Python
import xarray as xr
import datetime as dt
import earthaccess
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

Finding OPeNDAP URLs with PyDAP

The needed parameter to search for all Land Surface Temperature data from ECOSTRESS available through OPeNDAP is

Concept Collection ID = C2076114664-LPCLOUD

Data from the above collection is a Level 2 data product, meaning SWATH data, and all remote files span different longitudes and latitudes. In this case, the necessary first step is to filter the search for all relevant data URLs by a bounding box. Later on, a further subset by coordinate values will be done by OPeNDAP.

To learn how to find the concept collection id for a specific data product, click the button below:

Below are the required parameters to search for all OPeNDAP URLs using PyDAP's get_cmr_urls:

Python

ECOSTRESS_ccid = "C2076114664-LPCLOUD"
bounding_box = [-128.847656, 41.112469, -107.050781, 46.679594]
time_range = [dt.datetime(2025, 3, 1), dt.datetime(2025, 4, 30)]

# search for granules
cmr_urls = get_cmr_urls(ccid=ECOSTRESS_ccid, bounding_box = bounding_box, limit=1000, time_range=time_range) # limit by default = 50

EDL Authentication with earthaccess and OPeNDAP

There are various ways to authenticate with NASA, and here we will use earthaccess to retrieve a session object containing all required credentials to access data.

When using earthaccess to "login", you need to define a strategy and you have two options:

  1. If you already have a .netrc file with your EDL credentials stored in your machine, set strategy="netrc"
  2. If you DO NOT have a .netrc file with your EDL credentials, or you are not sure, do instead strategy="interactive"

Python
from earthaccess.exceptions import LoginStrategyUnavailable
try:
    auth = earthaccess.login(strategy="netrc", persist=True) 
except LoginStrategyUnavailable:
    # you will be prompted to add your EDL credentials
    auth = earthaccess.login(strategy="interactive", persist=True) 

# pass Token Authorization to a new Session.
my_session = session=auth.get_session()

The object my_session contains your EDL credentials, and it will be used to retrieve data from OPeNDAP. Moreover, by adding a persist=True as an argument to earthaccess.login, a .netrc is created to stored your EDL credentials in the machine for later reuse.

Use OPeNDAP to subset data by coordinate values and variable names

Subset by variable names

Below we use PyDAP to download the OPeNDAP DAP4 metadata. Pydap will create a Python representation of the dataset, including all variable names and their dimension, along with all metadata attributes associated with each variable. We will use this information to identify the variables of interest.

Python
pyds = open_url(cmr_urls[0], protocol="dap4", session=my_session)
pyds.tree()
HDF5 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

We are interested in only daylight data, with good quality information. What does good quality mean? For the purposes of this tutorial, we are interested in:

  • Daylight data
  • QAPercentCloudCover < 30%
  • QAPercentGoodQuality > 70%

To further filter the remote data by the flags above, we ONLY download the relevant variables from each remote file

Python
dap_to_netcdf(cmr_urls, session=my_session, output_path = output_path, 
              keep_variables=["/L2 LSTE Metadata/QAPercentCloudCover", 
                              "/L2 LSTE Metadata/QAPercentGoodQuality",
                              "/StandardMetadata/DayNightFlag"]
             )

The snippet of code above will download ONLY relevant metadata variables from ALL the remote files, and stored into individual files with a name matching the source file.

With the metadata variables already downloaded, we use the code below to identify which remote file satisfies are criteria:

Python
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))

In our example, only 16 remote files from the initial 194 satisfy our criteria!

Finally, we download the data of interest for all the granules in the time period which satisfy our quality criteria, but not without before removing all recently downloaded data from:

Terminal
$ cd $output_path
$ rm ECOv002_L2*.nc4

where $output_path should be replaced by the location of the files recently downloaded (this is necessary to avoid filename collision in the following step).

Subset by Variable Names

Finally, we are at the point where we want to download relevant data to Land Surface Temperature, along with many metadata flags. For this tutorial, we have the following variables of interest:

Python
keep_vars = ["/StandardMetadata/EastBoundingCoordinate", 
             "/StandardMetadata/SouthBoundingCoordinate", 
             "/StandardMetadata/NorthBoundingCoordinate",
             "/StandardMetadata/WestBoundingCoordinate",
             "/StandardMetadata/DayNightFlag",
             "/StandardMetadata/ImagePixels",
             "/StandardMetadata/ImagePixelSpacing",
             "/StandardMetadata/ImageLines",
             "/StandardMetadata/RangeBeginningDate",
             "/StandardMetadata/RangeBeginningTime",
             "/StandardMetadata/RangeEndingDate",
            "/StandardMetadata/RangeEndingTime",
             "/L2 LSTE Metadata/QAPercentCloudCover",
             "/L2 LSTE Metadata/QAPercentGoodQuality",
             "/SDS/QC", "/SDS/LST","/SDS/water_mask"
            ]

See code in action below:

References

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

Cite this Tutorial

Citation
Jimenez-Urias, M. A. (2026). Access Land Surface Temperature Data From ECOSTRESS Via OPeNDAP. Zenodo. https://doi.org/10.5281/zenodo.19477049
BibTeX
@misc{jimenez_urias_2026_19477049,
  author       = {Jimenez-Urias, Miguel Angel},
  title        = {Access Land Surface Temperature Data From
                   ECOSTRESS Via OPeNDAP
                  },
  month        = apr,
  year         = 2026,
  publisher    = {Zenodo},
  doi          = {10.5281/zenodo.19477049},
  url          = {https://doi.org/10.5281/zenodo.19477049},
}