116 hls nasa earthdata
Searching and Visualizing HLS Data from NASA Earthdata
The Harmonized Landsat and Sentinel-2 (HLS) project provides consistent 30-meter surface reflectance products from Landsat and Sentinel-2. The HLSL30 and HLSS30 products are distributed through NASA Earthdata as Cloud Optimized GeoTIFFs (COGs), which makes them suitable for cloud-native search, download, and interactive visualization.
This notebook demonstrates how to search HLS granules with earthaccess through leafmap, visualize granule footprints, and stream HLS true color and NDVI layers on an interactive map with TiTiler CMR.
Installation¶
Uncomment the following line to install the required packages if needed.
# %pip install -U "leafmap[raster]" earthaccess geopandas mapclassify
import leafmap
Sign in to NASA Earthdata¶
Searching public metadata does not always require authentication, but downloading protected HLS assets does. Create a NASA Earthdata Login account at urs.earthdata.nasa.gov if you do not already have one.
leafmap.nasa_data_login()
Define the search parameters¶
The example below searches for HLS granules near San Francisco, California, USA during summer 2025. HLSL30 is the Landsat product and HLSS30 is the Sentinel-2 product.
hls_collections = {
"HLSL30": "C2021957657-LPCLOUD",
"HLSS30": "C2021957295-LPCLOUD",
}
bbox = (-122.55, 37.68, -122.30, 37.84)
temporal = ("2025-06-01", "2025-08-31")
map_center = [37.7749, -122.4194]
Search HLSL30 granules¶
Set return_gdf=True to return the granule footprints as a GeoDataFrame in addition to the Earthaccess search results. The cloud_cover=(0, 10) parameter limits the results to granules with less than 10% cloud cover.
landsat_results, landsat_gdf = leafmap.nasa_data_search(
short_name="HLSL30",
version="2.0",
cloud_hosted=True,
bounding_box=bbox,
temporal=temporal,
cloud_cover=(0, 10),
count=20,
return_gdf=True,
)
landsat_gdf[["native-id", "BeginningDateTime", "EndingDateTime", "GranuleUR"]].head()
Search the matching Sentinel-2 HLS collection for the same area and date range.
sentinel_results, sentinel_gdf = leafmap.nasa_data_search(
short_name="HLSS30",
version="2.0",
cloud_hosted=True,
bounding_box=bbox,
temporal=temporal,
cloud_cover=(0, 10),
count=20,
return_gdf=True,
)
sentinel_gdf[["native-id", "BeginningDateTime", "EndingDateTime", "GranuleUR"]].head()
Visualize the search footprints¶
The footprints show the HLS MGRS tiles returned by NASA CMR for the selected area and time range.
m = leafmap.Map(center=map_center, zoom=9, height="700px")
m.add_basemap("Satellite")
m.add_gdf(
landsat_gdf,
layer_name="HLSL30 footprints",
style={"color": "#d7191c", "weight": 2, "fillOpacity": 0.05},
)
m.add_gdf(
sentinel_gdf,
layer_name="HLSS30 footprints",
style={"color": "#2c7bb6", "weight": 2, "fillOpacity": 0.05},
)
m
Visualize HLS true color imagery¶
Use the HLS collection concept ID with add_cmr_layer() to stream COG assets from NASA Earthdata through TiTiler CMR. The HLS true color composite uses red, green, and blue bands.
titiler_cmr_endpoint = "https://staging.openveda.cloud/api/titiler-cmr"
landsat_datetime = "2025-08-31T00:00:00Z/2025-08-31T23:59:59Z"
m = leafmap.Map(center=map_center, zoom=10, height="700px")
m.add_cmr_layer(
concept_id=hls_collections["HLSL30"],
datetime=landsat_datetime,
backend="rasterio",
bands=["B04", "B03", "B02"],
bands_regex="B[0-9][0-9]",
color_formula="Gamma RGB 3.5 Saturation 1.7 Sigmoidal RGB 15 0.35",
name="HLSL30 true color",
titiler_cmr_endpoint=titiler_cmr_endpoint,
zoom_to_layer=False,
)
m
Visualize NDVI¶
Band math expressions can be sent directly to TiTiler CMR. For HLSL30, NDVI uses near infrared band B05 and red band B04.
m = leafmap.Map(center=map_center, zoom=10, height="700px")
m.add_cmr_layer(
concept_id=hls_collections["HLSL30"],
datetime=landsat_datetime,
backend="rasterio",
expression="(B05-B04)/(B05+B04)",
bands_regex="B[0-9][0-9]",
rescale="-1,1",
colormap_name="rdylgn",
name="HLSL30 NDVI",
titiler_cmr_endpoint=titiler_cmr_endpoint,
zoom_to_layer=False,
)
m
Compare Landsat and Sentinel-2 HLS layers¶
Because HLSL30 and HLSS30 are harmonized to the same 30-meter grid, you can add both products to the same map and use the layer control to compare acquisition dates.
sentinel_datetime = "2025-08-31T00:00:00Z/2025-08-31T23:59:59Z"
m = leafmap.Map(center=map_center, zoom=10, height="700px")
for short_name, datetime in [
("HLSL30", landsat_datetime),
("HLSS30", sentinel_datetime),
]:
m.add_cmr_layer(
concept_id=hls_collections[short_name],
datetime=datetime,
backend="rasterio",
bands=["B04", "B03", "B02"],
bands_regex="B[0-9][0-9]",
color_formula="Gamma RGB 3.5 Saturation 1.7 Sigmoidal RGB 15 0.35",
name=f"{short_name} true color",
titiler_cmr_endpoint=titiler_cmr_endpoint,
zoom_to_layer=False,
)
m
Download selected HLS assets¶
Use keywords to download only selected band files from the returned granules. The example below is commented out to avoid downloading files unintentionally.
leafmap.nasa_data_download(
landsat_results[:1],
out_dir="data",
keywords=[".B04.tif", ".B03.tif", ".B02.tif", ".B05.tif"],
)