Common Metadata Repository (CMR) Search for ECOSTRESS Collection 2
Project description
Common Metadata Repository (CMR) Search for ECOSTRESS Collection 2
The ECOv002-CMR Python package is a utility for searching and downloading ECOSTRESS Collection 2 tiled data product granules using the Common Metadata Repository (CMR) API.
Gregory H. Halverson (they/them)
gregory.h.halverson@jpl.nasa.gov
NASA Jet Propulsion Laboratory 329G
Pre-Requisites
This package uses wget for file transfers.
On macOS, install wget with Homebrew:
brew install wget
Installation
Install the ECOv002-CMR package, with a dash in the name, from PyPi using pip:
pip install ECOv002-CMR
Authentication
ECOSTRESS data requires NASA Earthdata authentication. Set up your credentials using one of these methods:
Option 1: Create a .netrc file (recommended)
cat > ~/.netrc << EOF
machine urs.earthdata.nasa.gov
login YOUR_USERNAME
password YOUR_PASSWORD
EOF
chmod 600 ~/.netrc
Option 2: Environment variables
export EARTHDATA_USERNAME=YOUR_USERNAME
export EARTHDATA_PASSWORD=YOUR_PASSWORD
Register for a free account at urs.earthdata.nasa.gov if you don't have one.
Usage
Import the ECOv002_CMR package, with an underscore in the name:
import ECOv002_CMR
from datetime import date
Search for ECOSTRESS Granules
Use ECOSTRESS_CMR_search() to query the CMR API and get available granules without downloading:
from ECOv002_CMR import ECOSTRESS_CMR_search
# Search for Land Surface Temperature data
results = ECOSTRESS_CMR_search(
product="L2T_LSTE", # Product name
tile="11SLT", # Sentinel-2 tile ID
start_date=date(2025, 6, 1), # Start date
end_date=date(2025, 6, 30) # End date (optional, defaults to start_date)
)
# Results is a pandas DataFrame with columns:
# - product, variable, orbit, scene, tile
# - type (e.g., 'GeoTIFF Data', 'JSON Metadata')
# - granule, filename, URL
print(f"Found {len(results)} files")
print(results[['variable', 'orbit', 'scene', 'URL']].head())
# Filter for specific variables
lst_files = results[
(results['type'] == 'GeoTIFF Data') &
(results['variable'] == 'LST')
]
# Filter for specific orbit/scene (optional)
specific_granule = ECOSTRESS_CMR_search(
product="L2T_LSTE",
tile="11SLT",
start_date=date(2025, 6, 1),
orbit=52314, # Filter by orbit number
scene=3 # Filter by scene number
)
Download an ECOSTRESS Granule
Use download_ECOSTRESS_granule() to download all files for a specific granule:
from ECOv002_CMR import download_ECOSTRESS_granule
# Download a granule by date
granule = download_ECOSTRESS_granule(
product="L2T_LSTE",
tile="10UEV",
aquisition_date=date(2023, 8, 1),
parent_directory="./data" # Where to save (default: ~/data/ECOSTRESS)
)
# Download specific orbit/scene
granule = download_ECOSTRESS_granule(
product="L2T_LSTE",
tile="10UEV",
aquisition_date=date(2023, 8, 1),
orbit=52314,
scene=3
)
# The returned object is an ECOSTRESSGranule instance
print(granule)
Available Products
The package supports these ECOSTRESS Collection 2 products:
| Product Code | Description | Key Variables |
|---|---|---|
L2T_LSTE |
Land Surface Temperature & Emissivity | LST, LST_err, QC, EmisWB |
L2T_STARS |
Surface Reflectance & Albedo | NDVI, EVI, albedo, reflectance bands |
L3T_MET |
Meteorological Data | temperature, pressure, humidity |
L3T_SM |
Soil Moisture | soil moisture estimates |
L3T_SEB |
Surface Energy Balance | latent heat, sensible heat, net radiation |
L3T_JET |
Evapotranspiration Ensemble | ensemble ET products |
L4T_ESI |
Evaporative Stress Index | ESI, anomaly |
L4T_WUE |
Water Use Efficiency | WUE metrics |
Finding Your Tile
ECOSTRESS uses the Sentinel-2 MGRS tiling system. Find your tile using coordinates:
from sentinel_tiles import sentinel_tiles
from shapely.geometry import Point
# Find tile for a coordinate (lon, lat)
point = Point(-118.2437, 34.0522) # Los Angeles
tile = sentinel_tiles.nearest(point)
print(f"Tile: {tile}") # Output: 11SLT
Or browse the Sentinel-2 tiling grid.
Point Sampling
Sample ECOSTRESS data at specific geographic points. Choose from two approaches:
- Date Range Sampling (Recommended): Automatically find and sample all available ECOSTRESS acquisitions in a date range
- Datetime Sampling (Legacy): Sample at specific acquisition times if you already know them
Why Date Range Sampling?
ECOSTRESS acquisition times are not known in advance - they depend on the International Space Station's orbit and imaging schedule. The date range approach searches for all available data and samples it at your points, so you don't need to guess acquisition times.
Sample at Single Point
Extract values at specific coordinates from ECOSTRESS rasters:
from ECOv002_CMR import (
setup_earthdata_session,
sample_point_from_url,
ECOSTRESS_CMR_search
)
from datetime import date
# Set up authenticated session
session = setup_earthdata_session()
# Search for data
results = ECOSTRESS_CMR_search(
product="L2T_LSTE",
tile="11SLT",
start_date=date(2025, 6, 15)
)
# Get URL for LST variable
lst_files = results[
(results['type'] == 'GeoTIFF Data') &
(results['variable'] == 'LST')
]
url = lst_files.iloc[0]['URL']
# Sample at coordinate (lon, lat in WGS84)
value, metadata = sample_point_from_url(
session=session,
url=url,
lon=-118.2437, # Downtown LA
lat=34.0522
)
if value is not None:
print(f"LST: {value:.2f} K ({value - 273.15:.2f}°C)")
else:
print(f"Sampling failed: {metadata}")
Batch Sampling for All Available Acquisitions (Recommended)
Sample multiple points across all ECOSTRESS acquisitions in a date range with automatic temperature conversion:
import geopandas as gpd
from datetime import date
from shapely.geometry import Point
from ECOv002_CMR import sample_points_over_date_range
# Create GeoDataFrame with points (no datetime needed!)
data = {
'site_id': ['site_A', 'site_B', 'site_C'],
'geometry': [
Point(-118.24, 34.05), # Downtown LA
Point(-118.41, 33.94), # LAX Airport
Point(-118.14, 34.15) # Pasadena
]
}
gdf = gpd.GeoDataFrame(data, crs='EPSG:4326')
# Sample all available acquisitions in June 2025
# Uses default layers: ST_C (surface temp in Celsius), NDVI, albedo
results = sample_points_over_date_range(
geometry=gdf, # Can also be list of Point objects or GeoSeries
start_date=date(2025, 6, 1),
end_date=date(2025, 6, 30)
)
# Results DataFrame contains all point-acquisition combinations
# ST_C is automatically converted from Kelvin to Celsius!
print(results[['site_id', 'timestamp', 'ST_C', 'NDVI', 'albedo']])
# See how many acquisitions covered each site
print(results.groupby('site_id').size())
# Save results
results.to_csv('ecostress_samples.csv', index=False)
Available layers (defaults to ['ST_C', 'NDVI', 'albedo']):
ST_C- Surface Temperature in Celsius (auto-converted from LST)LST- Land Surface Temperature in KelvinNDVI- Normalized Difference Vegetation IndexEVI- Enhanced Vegetation Indexalbedo- Surface albedoSAVI- Soil Adjusted Vegetation IndexQC- Quality control flagsLST_err- LST uncertaintyEmisWB- Wideband emissivity
Custom layers example:
# Request specific layers
results = sample_points_over_date_range(
geometry=gdf,
start_date=date(2025, 6, 1),
end_date=date(2025, 6, 30),
layers=['ST_C', 'NDVI', 'EVI', 'QC'] # Custom selection
)
Using a list of points (no GeoDataFrame needed):
from shapely.geometry import Point
points = [
Point(-118.24, 34.05),
Point(-118.41, 33.94),
Point(-118.14, 34.15)
]
results = sample_points_over_date_range(
geometry=points, # Just a list of Point objects!
start_date=date(2025, 6, 1),
end_date=date(2025, 6, 30)
)
### Batch Sampling with Specific Datetimes
If you do know specific acquisition times you want to sample:
```python
import geopandas as gpd
from datetime import datetime
from shapely.geometry import Point
from ECOv002_CMR import sample_points_from_geodataframe
# Create GeoDataFrame with points and datetimes
data = {
'site_id': ['site_A', 'site_B', 'site_C'],
'datetime': [
datetime(2025, 6, 15, 12, 0),
datetime(2025, 6, 20, 14, 0),
datetime(2025, 6, 25, 10, 0)
],
'geometry': [
Point(-118.24, 34.05), # Downtown LA
Point(-118.41, 33.94), # LAX Airport
Point(-118.14, 34.15) # Pasadena
]
}
gdf = gpd.GeoDataFrame(data, crs='EPSG:4326')
# Define products and variables to sample
products = {
'L2T_LSTE': 'LST', # Land Surface Temperature
'L2T_STARS': ['NDVI', 'albedo'] # NDVI and Albedo
}
# Sample all points
results = sample_points_from_geodataframe(
gdf=gdf,
products=products,
date_buffer_days=3, # Search ±3 days around each datetime
verbose=True
)
# Results DataFrame contains sampled values
print(results[['site_id', 'timestamp', 'LST', 'NDVI', 'albedo']])
# Convert temperature to Celsius
results['LST_celsius'] = results['LST'] - 273.15
# Save results
results.to_csv('ecostress_samples.csv', index=False)
This approach uses date_buffer_days to search for granules near each specified datetime. Useful when you have specific target times but want to allow some flexibility.
See examples/geodataframe_sampling_demo.py for the recommended date-range approach.
Working with Multiple Variables
Search across multiple products and combine results:
from ECOv002_CMR import ECOSTRESS_CMR_search
# Products and their variables
products = {
"L2T_LSTE": ["LST", "QC"],
"L2T_STARS": ["NDVI", "albedo"]
}
tile = "11SLT"
start = date(2025, 6, 1)
end = date(2025, 6, 30)
all_files = {}
for product, variables in products.items():
results = ECOSTRESS_CMR_search(
product=product,
tile=tile,
start_date=start,
end_date=end
)
for variable in variables:
var_files = results[
(results['type'] == 'GeoTIFF Data') &
(results['variable'] == variable)
]
all_files[variable] = var_files
print(f"{variable}: {len(var_files)} files")
Advanced Point Sampling Options
Advanced Point Sampling Options
Custom tile specification:
# Pre-specify tiles instead of auto-detecting
gdf['tile'] = ['11SLT', '11SLT', '11SLT']
results = sample_points_from_geodataframe(
gdf=gdf,
products=products,
tile_col='tile' # Use pre-specified tiles
)
Find Sentinel-2 tile for a coordinate:
from ECOv002_CMR import find_sentinel2_tile
tile = find_sentinel2_tile(lon=-118.24, lat=34.05)
print(f"Tile: {tile}") # Output: 11SLT
Custom datetime column names:
results = sample_points_from_geodataframe(
gdf=gdf,
products=products,
geometry_col='location', # Custom geometry column
datetime_col='timestamp' # Custom datetime column
)
See examples/point_sampling_demo.py for single-point sampling workflow.
Date Formats
Dates can be specified as date objects or strings:
from datetime import date
from dateutil import parser
# Using date objects (recommended)
results = ECOSTRESS_CMR_search(
product="L2T_LSTE",
tile="11SLT",
start_date=date(2025, 6, 1),
end_date=date(2025, 6, 30)
)
# Using date strings
results = ECOSTRESS_CMR_search(
product="L2T_LSTE",
tile="11SLT",
start_date="2025-06-01",
end_date="2025-06-30"
)
Error Handling
from ECOv002_CMR import ECOSTRESS_CMR_search
try:
results = ECOSTRESS_CMR_search(
product="L2T_LSTE",
tile="11SLT",
start_date=date(2025, 6, 1)
)
if results.empty:
print("No granules found for specified criteria")
else:
print(f"Found {len(results)} files")
except ValueError as e:
print(f"Invalid parameter: {e}")
except Exception as e:
print(f"Search failed: {e}")
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ecov002_cmr-1.3.0.tar.gz.
File metadata
- Download URL: ecov002_cmr-1.3.0.tar.gz
- Upload date:
- Size: 2.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa49a21aa53069e213a018071e5af10272371239e9795e050a0edd0439b0c439
|
|
| MD5 |
0204e886247b41bd10ef8e05df378db6
|
|
| BLAKE2b-256 |
d920fe7fc9ea87324e17b49231e909165880060db65b9253131de0eec4911726
|
File details
Details for the file ecov002_cmr-1.3.0-py3-none-any.whl.
File metadata
- Download URL: ecov002_cmr-1.3.0-py3-none-any.whl
- Upload date:
- Size: 36.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7bad750e5fcaaea6e2c91f6ed712e0423d8ca352b32be93309db13d47f0d954f
|
|
| MD5 |
85135f6e6ce4ba1aadb09984df011823
|
|
| BLAKE2b-256 |
8229a4c588baa79c14c1fa412f543e806131c9e8438f1fe4a4fa519b066203b2
|