Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
318 changes: 318 additions & 0 deletions docs/notebooks/116_hls_nasa_earthdata.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=notebooks/116_hls_nasa_earthdata.ipynb)\n",
"[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/notebooks/116_hls_nasa_earthdata.ipynb)\n",
"[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n",
"\n",
"**Searching and Visualizing HLS Data from NASA Earthdata**\n",
"\n",
"The [Harmonized Landsat and Sentinel-2](https://www.earthdata.nasa.gov/data/projects/hls) (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.\n",
"\n",
"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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"Uncomment the following line to install the required packages if needed."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# %pip install -U \"leafmap[raster]\" earthaccess geopandas mapclassify"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import leafmap"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sign in to NASA Earthdata\n",
"\n",
"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](https://urs.earthdata.nasa.gov) if you do not already have one."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"leafmap.nasa_data_login()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define the search parameters\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hls_collections = {\n",
" \"HLSL30\": \"C2021957657-LPCLOUD\",\n",
" \"HLSS30\": \"C2021957295-LPCLOUD\",\n",
"}\n",
"\n",
"bbox = (-122.55, 37.68, -122.30, 37.84)\n",
"temporal = (\"2025-06-01\", \"2025-08-31\")\n",
"map_center = [37.7749, -122.4194]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Search HLSL30 granules\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"landsat_results, landsat_gdf = leafmap.nasa_data_search(\n",
" short_name=\"HLSL30\",\n",
" version=\"2.0\",\n",
" cloud_hosted=True,\n",
" bounding_box=bbox,\n",
" temporal=temporal,\n",
" cloud_cover=(0, 10),\n",
" count=20,\n",
" return_gdf=True,\n",
")\n",
"\n",
"landsat_gdf[[\"native-id\", \"BeginningDateTime\", \"EndingDateTime\", \"GranuleUR\"]].head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Search the matching Sentinel-2 HLS collection for the same area and date range."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sentinel_results, sentinel_gdf = leafmap.nasa_data_search(\n",
" short_name=\"HLSS30\",\n",
" version=\"2.0\",\n",
" cloud_hosted=True,\n",
" bounding_box=bbox,\n",
" temporal=temporal,\n",
" cloud_cover=(0, 10),\n",
" count=20,\n",
" return_gdf=True,\n",
")\n",
"\n",
"sentinel_gdf[[\"native-id\", \"BeginningDateTime\", \"EndingDateTime\", \"GranuleUR\"]].head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualize the search footprints\n",
"\n",
"The footprints show the HLS MGRS tiles returned by NASA CMR for the selected area and time range."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m = leafmap.Map(center=map_center, zoom=9, height=\"700px\")\n",
"m.add_basemap(\"Satellite\")\n",
"m.add_gdf(\n",
" landsat_gdf,\n",
" layer_name=\"HLSL30 footprints\",\n",
" style={\"color\": \"#d7191c\", \"weight\": 2, \"fillOpacity\": 0.05},\n",
")\n",
"m.add_gdf(\n",
" sentinel_gdf,\n",
" layer_name=\"HLSS30 footprints\",\n",
" style={\"color\": \"#2c7bb6\", \"weight\": 2, \"fillOpacity\": 0.05},\n",
")\n",
"m"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualize HLS true color imagery\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"titiler_cmr_endpoint = \"https://staging.openveda.cloud/api/titiler-cmr\"\n",
"landsat_datetime = \"2025-08-31T00:00:00Z/2025-08-31T23:59:59Z\"\n",
"\n",
"m = leafmap.Map(center=map_center, zoom=10, height=\"700px\")\n",
"m.add_cmr_layer(\n",
" concept_id=hls_collections[\"HLSL30\"],\n",
" datetime=landsat_datetime,\n",
" backend=\"rasterio\",\n",
" bands=[\"B04\", \"B03\", \"B02\"],\n",
" bands_regex=\"B[0-9][0-9]\",\n",
" color_formula=\"Gamma RGB 3.5 Saturation 1.7 Sigmoidal RGB 15 0.35\",\n",
" name=\"HLSL30 true color\",\n",
" titiler_cmr_endpoint=titiler_cmr_endpoint,\n",
" zoom_to_layer=False,\n",
")\n",
"m"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualize NDVI\n",
"\n",
"Band math expressions can be sent directly to TiTiler CMR. For HLSL30, NDVI uses near infrared band B05 and red band B04."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m = leafmap.Map(center=map_center, zoom=10, height=\"700px\")\n",
"m.add_cmr_layer(\n",
" concept_id=hls_collections[\"HLSL30\"],\n",
" datetime=landsat_datetime,\n",
" backend=\"rasterio\",\n",
" expression=\"(B05-B04)/(B05+B04)\",\n",
" bands_regex=\"B[0-9][0-9]\",\n",
" rescale=\"-1,1\",\n",
" colormap_name=\"rdylgn\",\n",
" name=\"HLSL30 NDVI\",\n",
" titiler_cmr_endpoint=titiler_cmr_endpoint,\n",
" zoom_to_layer=False,\n",
")\n",
"m"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Compare Landsat and Sentinel-2 HLS layers\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sentinel_datetime = \"2025-08-31T00:00:00Z/2025-08-31T23:59:59Z\"\n",
"\n",
"m = leafmap.Map(center=map_center, zoom=10, height=\"700px\")\n",
"for short_name, datetime in [\n",
" (\"HLSL30\", landsat_datetime),\n",
" (\"HLSS30\", sentinel_datetime),\n",
"]:\n",
" m.add_cmr_layer(\n",
" concept_id=hls_collections[short_name],\n",
" datetime=datetime,\n",
" backend=\"rasterio\",\n",
" bands=[\"B04\", \"B03\", \"B02\"],\n",
" bands_regex=\"B[0-9][0-9]\",\n",
" color_formula=\"Gamma RGB 3.5 Saturation 1.7 Sigmoidal RGB 15 0.35\",\n",
" name=f\"{short_name} true color\",\n",
" titiler_cmr_endpoint=titiler_cmr_endpoint,\n",
" zoom_to_layer=False,\n",
" )\n",
"m"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Download selected HLS assets\n",
"\n",
"Use `keywords` to download only selected band files from the returned granules. The example below is commented out to avoid downloading files unintentionally."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"leafmap.nasa_data_download(\n",
" landsat_results[:1],\n",
" out_dir=\"data\",\n",
" keywords=[\".B04.tif\", \".B03.tif\", \".B02.tif\", \".B05.tif\"],\n",
")"
Comment on lines +289 to +293
]
}
],
"metadata": {
"kernelspec": {
"display_name": "geo",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ plugins:
"notebooks/101_nasa_opera.ipynb",
"notebooks/102_fused.ipynb",
"notebooks/107_copernicus.ipynb",
"notebooks/116_hls_nasa_earthdata.ipynb",
"maplibre/3d_pmtiles.ipynb",
"maplibre/animate_a_line.ipynb",
"maplibre/copernicus.ipynb",
Expand Down Expand Up @@ -416,3 +417,4 @@ nav:
- notebooks/113_titiler_cmr.ipynb
- notebooks/114_nasa_fire.ipynb
- notebooks/115_terrascope.ipynb
- notebooks/116_hls_nasa_earthdata.ipynb
Loading