diff --git a/_quarto.yml b/_quarto.yml
index c8e0b8bd..8eaed6ce 100644
--- a/_quarto.yml
+++ b/_quarto.yml
@@ -212,12 +212,17 @@ website:
href: quarto_text/SWOT.html#workshops-webinars
- section: quarto_text/GIS.qmd
contents:
- - text: "NASA Earthdata (EGIS) Services"
- href: notebooks/GIS/SWOT_Rivers_EGIS_Services.md
+ - section: "NASA EGIS"
+ href: quarto_text/GIS.html#nasa-earthdata-gis-egis
+ contents:
+ - text: "SWOT"
+ href: notebooks/GIS/SWOT_Rivers_EGIS_Services.md
+ - text: "OPERA DSWx"
+ href: notebooks/GIS/OPERA_DSWx-HLS_EGIS_Services.md
- text: "StoryMaps"
- href: quarto_text/GIS.html#gis-storymaps-of-select-datasets
- - text: "GIS Walkthroughs"
- href: quarto_text/GIS.html#gis-walkthroughs
+ href: quarto_text/GIS.html#storymaps
+ - text: "Other GIS Tutorials"
+ href: quarto_text/GIS.html#other-gis-tutorials
- text: "GIS Jupyter Notebook Tutorials"
href: quarto_text/GIS.html#gis-jupyter-notebook-tutorials
- section: quarto_text/CloudvsLocalWorkflows.qmd
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_1.png b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_1.png
new file mode 100644
index 00000000..1ae87a03
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_1.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_2.png b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_2.png
new file mode 100644
index 00000000..b90da0fd
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_2.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_3.png b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_3.png
new file mode 100644
index 00000000..dae4f2d0
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_3.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_4.png b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_4.png
new file mode 100644
index 00000000..665d3195
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_4.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_5.png b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_5.png
new file mode 100644
index 00000000..6165e99b
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_ArcGIS_5.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_1.png b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_1.png
new file mode 100644
index 00000000..e53ea345
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_1.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_2.png b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_2.png
new file mode 100644
index 00000000..3db3c721
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_2.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_3.png b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_3.png
new file mode 100644
index 00000000..184d8a9b
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_3.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_4.png b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_4.png
new file mode 100644
index 00000000..07ea945a
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_4.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_5.png b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_5.png
new file mode 100644
index 00000000..1ff47ada
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_5.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_6.png b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_6.png
new file mode 100644
index 00000000..87f18825
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_MapViewer_6.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_1.png b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_1.png
new file mode 100644
index 00000000..cba87764
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_1.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_2.png b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_2.png
new file mode 100644
index 00000000..abae4788
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_2.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_3.png b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_3.png
new file mode 100644
index 00000000..15537445
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_3.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_4.png b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_4.png
new file mode 100644
index 00000000..fa749ccf
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_4.png differ
diff --git a/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_5.png b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_5.png
new file mode 100644
index 00000000..64c1af1c
Binary files /dev/null and b/images/EGIS_OPERA_Images/OPERA_EGIS_QGIS_5.png differ
diff --git a/notebooks/GIS/OPERA_DSWx-HLS_EGIS_Services.md b/notebooks/GIS/OPERA_DSWx-HLS_EGIS_Services.md
new file mode 100644
index 00000000..30bc9b7f
--- /dev/null
+++ b/notebooks/GIS/OPERA_DSWx-HLS_EGIS_Services.md
@@ -0,0 +1,15 @@
+# Earthdata GIS OPERA DSWx-HLS Image Services
+
+The OPERA DSWx-HLS Image Service is available in both the ArcGIS REST Endpoint and Open Geospatial Consortium (OGC) Endpoint.
+
+The following are the available service endpoints for each OPERA DSWx-HLS version and type:
+
+### OPERA_L3_DSWX-HLS_V1_B01_WTR (01/01/23 - Present)
+[Image Service](https://gis.earthdata.nasa.gov/portal/home/item.html?id=59caecb96b5b42dd85aa3001a10f7786)
+
+[Web Map Service (WMS)](https://gis.earthdata.nasa.gov/portal/home/item.html?id=7e40454ca08d4eb39d5e6693109367eb)
+
+### OPERA_L3_DSWX-HLS_V1_B03_CONF (01/01/23 - Present)
+[Image Service](https://gis.earthdata.nasa.gov/portal/home/item.html?id=f417d9093b714c06951f4c4bdf28e64e)
+
+[Web Map Service (WMS)](https://gis.earthdata.nasa.gov/portal/home/item.html?id=f2c0db6677c34d739d6591cc9a63bfb7)
\ No newline at end of file
diff --git a/notebooks/GIS/OPERA_EGIS_access.ipynb b/notebooks/GIS/OPERA_EGIS_access.ipynb
new file mode 100644
index 00000000..9ec92f1e
--- /dev/null
+++ b/notebooks/GIS/OPERA_EGIS_access.ipynb
@@ -0,0 +1,146 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Earthdata GIS OPERA DSWx-HLS Image Service Access\n",
+ "
\n",
+ "\n",
+ "Author(s): Nicholas Tarpinian, Catalina Taglialatela *(NASA PO.DAAC)*\n",
+ "\n",
+ "_Created: 2026-06-01_
\n",
+ "_Last updated: 2026-06-23_\n",
+ "\n",
+ "Versions used: *ArcGIS Pro 3.6.1*\n",
+ "\n",
+ "## Summary\n",
+ "\n",
+ "This guide shows how to connect using GIS applications to the recently released NASA [Earthdata GIS (EGIS)](https://gis.earthdata.nasa.gov/portal/home/index.html) Image Service for Observational Products for End-Users from Remote Sensing Analysis (OPERA) Dynamic Surface Water eXtent (DSWx) Harmonized Landsat Sentinel-2 (HLS), namely the [OPERA_L3_DSWX-HLS_V1](https://www.earthdata.nasa.gov/data/catalog/pocloud-opera-l3-dswx-hls-v1-1.0) collection.\n",
+ "\n",
+ "- [OPERA_L3_DSWX-HLS_V1_B01_WTR](https://gis.earthdata.nasa.gov/portal/home/item.html?id=59caecb96b5b42dd85aa3001a10f7786) \n",
+ "\n",
+ "- [OPERA_L3_DSWX-HLS_V1_B03_CONF](https://gis.earthdata.nasa.gov/portal/home/item.html?id=f417d9093b714c06951f4c4bdf28e64e)\n",
+ "\n",
+ "\n",
+ "For more details on these services and the latest available services, please visit the EGIS OPERA Image Service page (_insert link when ready_).\n",
+ "\n",
+ "\n",
+ "------"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Learning Objectives:\n",
+ "- Connecting to the OPERA DSWx-HLS Image Service through the Map Viewer and ArcGIS Pro (QGIS has limitations - see end of tutorial).\n",
+ "- Exploring the collection metadata and visualizing the data in an interactive map.\n",
+ "\n",
+ "------"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Map Viewer\n",
+ "\n",
+ "You can quickly view the OPERA DSWx-HLS Image Service through the dataset landing page ([WTR landing page](https://gis.earthdata.nasa.gov/portal/home/item.html?id=59caecb96b5b42dd85aa3001a10f7786), [CONF landing page](https://gis.earthdata.nasa.gov/portal/home/item.html?id=f417d9093b714c06951f4c4bdf28e64e)) by selecting `Open in Map Viewer`.\n",
+ "\n",
+ "

\n",
+ "\n",
+ "Once open you can **expand the time scale** at the bottom to view the observed granules available.\n",
+ "\n",
+ " 
\n",
+ "\n",
+ "For the **pop-ups** to display correctly when selecting a granule: on the right hand side, select the `Pop-ups` option; then de-select both `Ignore NoData areas` and `Display topmost image only` to get all availble observations for the selected granule.\n",
+ "\n",
+ " 
\n",
+ "\n",
+ "The inital view once loaded will showcase the **overview layer**. When zooming in, the **source raster** will become available on the area of interest. Selecting a granule will pop up the granule attributes with the latest observations. In the corner of the pop-up, it will highlight the record amount available based on the time scale selected.\n",
+ "\n",
+ " 
\n",
+ "\n",
+ "To further explore the data, you can filter/query the data by selecting the `Filter` option on the right hand side. This will allow you to query the data based on the available attributes.\n",
+ "\n",
+ " 
\n",
+ "\n",
+ " 
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## ArcGIS Pro\n",
+ "\n",
+ "To connect to the Image Service in ArcGIS Pro, first grab the endpoint URL: https://gis.earthdata.nasa.gov/image/rest/services/C2617126679-POCLOUD/OPERA_L3_DSWX-HLS_V1_B01_WTR/ImageServer from the dataset landing page as shown in the image below:\n",
+ "\n",
+ " 
\n",
+ "\n",
+ "Next step is to open ArcGIS Pro and select the `Add Data` tab. \n",
+ "\n",
+ " 
\n",
+ "\n",
+ "Then within the `Add Data` section you have the option of loading the Image Service of the REST endpoint `From Path`. \n",
+ "\n",
+ " 
\n",
+ "\n",
+ "Then entering the Image Service endpoint within the `Path`.\n",
+ "\n",
+ " 
\n",
+ "\n",
+ "Once the data is added to the map, you can start exploring the Image Service.\n",
+ "\n",
+ " 
\n",
+ "\n",
+ "**The `OPERA_L3_DSWX-HLS_V1_B01_WTR` is best observed with a reduced time scale (e.g. using the time slider). Overview layers will be available to view at a global extent of the collection, but the source raster will only be available when zoomed in.**\n",
+ "\n",
+ "**Please note: If querying via `Select By Attributes`, certain queries may timeout depending on the attribute/variable used due to the amount of records.**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## QGIS\n",
+ "\n",
+ "There are current limitations with connecting to the Image Service in QGIS.\n",
+ "\n",
+ "Visit this StoryMap for more details on the limitations of QGIS accessibility: https://gis.earthdata.nasa.gov/portal/apps/storymaps/stories/67b9468490244f6998a19659eed1a1d9"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Programmatic Access\n",
+ "\n",
+ "For programmatic access examples, see `OPERA_EGIS_access_Python.ipynb` in the [PO.DAAC Cookbook](../notebooks/GIS/OPERA_EGIS_access_Python.ipynb)."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "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.13.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/notebooks/GIS/OPERA_EGIS_access_Python.ipynb b/notebooks/GIS/OPERA_EGIS_access_Python.ipynb
new file mode 100644
index 00000000..17293c0b
--- /dev/null
+++ b/notebooks/GIS/OPERA_EGIS_access_Python.ipynb
@@ -0,0 +1,982 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "1e68b311",
+ "metadata": {},
+ "source": [
+ "# Earthdata GIS OPERA DSWx-HLS Image Service Access\n",
+ "
\n",
+ "\n",
+ "Author(s): Nicholas Tarpinian, Brandi Downs, Catalina Taglialatela *(NASA PO.DAAC)*\n",
+ "\n",
+ "_Created: 2026-06-01_
\n",
+ "_Last Updated: 2026-06-24_
\n",
+ "\n",
+ "\n",
+ "\n",
+ "## Summary\n",
+ "\n",
+ "This guide shows how to programmatically (**`Python`**) connect to, explore, and plot using the recently released NASA [**Earthdata GIS (EGIS)**](https://gis.earthdata.nasa.gov/portal/home/index.html) Image Service for Observational Products for End-Users from Remote Sensing Analysis (OPERA) Dynamic Surface Water eXtent (DSWx) Harmonized Landsat Sentinel-2 (HLS), namely the [OPERA_L3_DSWX-HLS_V1](https://www.earthdata.nasa.gov/data/catalog/pocloud-opera-l3-dswx-hls-v1-1.0) collection. **This allows users to interactively explore and visualize DSWx-HLS data directly from the Image Service without having to download and manage data files locally.**\n",
+ "\n",
+ "Available EGIS services for OPERA DSWx-HLS at this time include:\n",
+ "\n",
+ "- [OPERA_L3_DSWX-HLS_V1_B01_WTR](https://gis.earthdata.nasa.gov/portal/home/item.html?id=59caecb96b5b42dd85aa3001a10f7786) \n",
+ "\n",
+ "- [OPERA_L3_DSWX-HLS_V1_B03_CONF](https://gis.earthdata.nasa.gov/portal/home/item.html?id=f417d9093b714c06951f4c4bdf28e64e)\n",
+ "\n",
+ "This tutorial focuses on the **B01_WTR** layer but a similar approahc can be used with the **B03_CONF** layer.\n",
+ "\n",
+ "\n",
+ "For more details on these services and the latest available services, please visit the [EGIS OPERA Image Service page](https://gis.earthdata.nasa.gov/portal/home/group.html?id=780767a6cee24331baae8557c78d2e8a).\n",
+ "\n",
+ "------"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b5322160",
+ "metadata": {},
+ "source": [
+ "### Learning Objectives:\n",
+ "- **Programmatic Access:** Connect to and interact with the OPERA DSWx-HLS REST Image Service API from EGIS using Python.\n",
+ "- **Service Discovery:** Programmatically retrieve metadata and capabilities of the Image Service.\n",
+ "- **User Configuration:** Define your geographic area of interest, time range, and data fields.\n",
+ "- **Tabular Data Extraction:** Query the ArcGIS REST endpoint and parse the resulting Image Service metadata into a viewable table.\n",
+ "- **Interactive Exploration:** Visualize the selected tiles for your use case to get oriented and assess if sufficient data is available given your specified filtering.\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ed1bc57e",
+ "metadata": {},
+ "source": [
+ "### Import libraries"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "ee51ad88",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "import requests\n",
+ "import json\n",
+ "import pandas as pd\n",
+ "import numpy as np\n",
+ "import folium\n",
+ "import xyzservices.providers as xyz\n",
+ "import base64\n",
+ "from IPython.display import display\n",
+ "from PIL import Image\n",
+ "from io import BytesIO"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "be1573eb",
+ "metadata": {},
+ "source": [
+ "The OPERA EGIS Image Service endpoint can be queried through the ArcGIS REST endpoint. Find the URL of the service on the [content landing page](https://gis.earthdata.nasa.gov/portal/home/item.html?id=59caecb96b5b42dd85aa3001a10f7786), in the bottom right corner of the service landing page under `URL` as shown below:\n",
+ "\n",
+ " 
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "960f88d9",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Helper Functions\n",
+ "We first define the following helper functions to handle time conversions, API requests, pagination, coordinate transformations, and scene validation.\n",
+ "\n",
+ "- `to_epoch_ms`: When we search for a date like `\"2025-01-01\"`, the EGIS (ArcGIS) REST API cannot read this standard text format. This function translates our human-readable date into Unix Epoch Time (the number of milliseconds since January 1, 1970). This ensures we are speaking the exact numerical language the server's backend database requires.\n",
+ "- `get_json`: This function acts as a safety wrapper for requesting our data. It forces the server to return machine-readable JSON (instead of an HTML webpage) and enforces a 2-minute timeout to prevent your notebook from freezing on large queries. If the server encounters an error, it will raise a clear exception with the error message, making debugging easier.\n",
+ "- `query_all_features`: ArcGIS REST services often limit the number of records returned in a single request. This function automatically handles pagination by repeatedly requesting batches of results and combining them into a single list, ensuring all matching scenes are retrieved.\n",
+ "- `make_clickable`: Converts download URLs into clickable hyperlinks when displaying query results in a Pandas table.\n",
+ "- `scene_has_valid_data`: Determines whether an exported scene contains any valid pixels within the requested bounding box. This is used to remove scenes whose footprints intersect the AOI but whose valid raster data falls entirely outside the area of interest.\n",
+ "\n",
+ "Most users only need to change the `URL=` in this code block when wishing to query a different EGIS REST API."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "0e55f242",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "OPERA DSWx-HLS Image Service ready to query.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Image Service URL\n",
+ "URL = \"https://gis.earthdata.nasa.gov/image/rest/services/C2617126679-POCLOUD/OPERA_L3_DSWX-HLS_V1_B01_WTR/ImageServer\"\n",
+ "\n",
+ "def to_epoch_ms(date_str):\n",
+ " \"\"\"Converts a standard date string to Epoch milliseconds.\"\"\"\n",
+ " return int(pd.Timestamp(date_str, tz='UTC').timestamp() * 1000)\n",
+ "\n",
+ "def get_json(url, params=None):\n",
+ " \"\"\"Sends a GET request and parses the JSON, with a 2-minute timeout for large queries.\"\"\"\n",
+ " p = dict(params or {})\n",
+ " p.setdefault('f', 'json')\n",
+ " r = requests.get(url, params=p, timeout=120)\n",
+ " r.raise_for_status()\n",
+ " data = r.json()\n",
+ " if 'error' in data:\n",
+ " raise RuntimeError(f\"Server Error: {data['error']}\")\n",
+ " return data\n",
+ "\n",
+ "# Retrieve all matching features by requesting results in batches and combining them into a single list.\n",
+ "# This is useful when a query returns more records than the server's maximum records-per-request limit. \n",
+ "# For example, large spatial extents or long time series may contain thousands of scenes.\n",
+ "# The function uses pagination to retrieve all records.\n",
+ "def query_all_features(url, params, page_size=1000):\n",
+ " \"\"\"Query all records from an ArcGIS REST endpoint using pagination.\"\"\"\n",
+ " all_features = []\n",
+ " offset = 0\n",
+ " while True:\n",
+ " p = dict(params)\n",
+ " p[\"resultOffset\"] = offset\n",
+ " p[\"resultRecordCount\"] = page_size\n",
+ " data = get_json(url, params=p)\n",
+ " features = data.get(\"features\", [])\n",
+ " all_features.extend(features)\n",
+ " if len(features) < page_size:\n",
+ " break\n",
+ " offset += page_size\n",
+ " return all_features\n",
+ "\n",
+ "# Helper function to convert the string into an HTML link for Download Link\n",
+ "def make_clickable(val):\n",
+ " if isinstance(val, str) and val.startswith(\"http\"):\n",
+ " return f'Download Granule'\n",
+ " return val\n",
+ "\n",
+ "# Check whether an exported image contains any valid data pixels within the requested bounding box.\n",
+ "# In the exported image, pixels with no data values ('255' in the DSWx WTR dataset) are fully transparent (alpha=0).\n",
+ "def scene_has_valid_data(resp_content, min_valid_pixels=1):\n",
+ " img = Image.open(BytesIO(resp_content)).convert(\"RGBA\")\n",
+ " alpha = np.asarray(img.getchannel(\"A\"))\n",
+ " valid_pixel_count = np.count_nonzero(alpha > 0)\n",
+ " return valid_pixel_count >= min_valid_pixels\n",
+ "\n",
+ "print(\"OPERA DSWx-HLS Image Service ready to query.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5458d957",
+ "metadata": {},
+ "source": [
+ "### Information about the Image Service\n",
+ "Before querying the data, it is helpful to understand what the service provides. This block queries the service's base endpoint to retrieve the available metadata fields, server limits, and the total date range of the dataset. \n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "f55d7415",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Grabbing Image Service Metadata...\n",
+ "\n",
+ "Service name : C2617126679-POCLOUD/OPERA_L3_DSWX-HLS_V1_B01_WTR\n",
+ "Available Fields : ['objectid', 'name', 'minps', 'maxps', 'lowps', 'highps', 'category', 'centerx', 'centery', 'shape', 'processingdate', 'tileid', 'datetime', 'productgenerationdatetime', 'cloud_coverage', 'spacecraft_name', 'downloadurl', 'urldisplay']\n",
+ "Max Export Size : 2048 x 2048 pixels\n",
+ "Service Capabilities : Catalog,Mensuration,Image,Metadata\n",
+ "Dataset Starts : 2023-01-01 00:29:59\n",
+ "Dataset Ends : 2026-06-17 23:56:09\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Grabbing Image Service Metadata...\\n\")\n",
+ "\n",
+ "meta = get_json(URL)\n",
+ "\n",
+ "print(f\"Service name : {meta.get('name')}\")\n",
+ "\n",
+ "# Available metadata fields:\n",
+ "fields = [f.get('name') for f in meta.get('fields', [])]\n",
+ "print(f\"Available Fields : {fields}\")\n",
+ "\n",
+ "# Server Limits (How big of an image can I request?)\n",
+ "max_h = meta.get('maxImageHeight')\n",
+ "max_w = meta.get('maxImageWidth')\n",
+ "print(f\"Max Export Size : {max_w} x {max_h} pixels\")\n",
+ "\n",
+ "# Capabilities (What can this API do?)\n",
+ "print(f\"Service Capabilities : {meta.get('capabilities')}\")\n",
+ "\n",
+ "# Time Info (What is the date range?)\n",
+ "time_info = meta.get('timeInfo', {})\n",
+ "start_ts = time_info.get('timeExtent', [0])[0]\n",
+ "end_ts = time_info.get('timeExtent', [0, 0])[1]\n",
+ "if start_ts:\n",
+ " print(f\"Dataset Starts : {pd.to_datetime(start_ts, unit='ms')}\")\n",
+ " print(f\"Dataset Ends : {pd.to_datetime(end_ts, unit='ms')}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "333b8332-f64a-4d85-b665-105992ab1ba2",
+ "metadata": {},
+ "source": [
+ "(To learn more about image _Service Capabilities_, [see here](https://doc.esri.com/en/arcgis-enterprise/latest/administer/key-concepts-for-image-services.html?pivots=os-windows#:~:text=Details-,Imaging,-Always%20enabled.%20Allows\").)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6eda9d03",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### User Configuration\n",
+ "**Define your search parameters here.** Adjust the Bounding Box (`BBOX`), date range (`START_DATE`, `END_DATE`), and maximum cloud coverage (`MAX_CLOUD_COVERAGE`) to target your specific Area of Interest (AOI) and use case. _Tip: You can use [bbox finder](http://bboxfinder.com/) to search for an area of interest coordinates._"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "dfd65909",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "# Define Area of Interest (xmin, ymin, xmax, ymax)\n",
+ "# For this example, we are using Lake Mead, USA\n",
+ "BBOX = \"-114.90,35.90,-113.80,36.60\" \n",
+ "\n",
+ "# Define Date Range (YYYY-MM-DD)\n",
+ "START_DATE = \"2025-01-01\"\n",
+ "END_DATE = \"2025-01-10\"\n",
+ "\n",
+ "# Define maximum acceptable Cloud Coverage (Percentage)\n",
+ "MAX_CLOUD_COVERAGE = 10 \n",
+ "\n",
+ "# Define specific fields from Availalbe Fields above to return in the table\n",
+ "FIELDS = \"objectid,name,datetime,cloud_coverage,tileid,spacecraft_name,downloadurl\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f9d497f6",
+ "metadata": {},
+ "source": [
+ "### Query & Display Data Table\n",
+ "Using the parameters defined above, we will query the OPERA DSWx-HLS Image Service catalog."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "aac59a05",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Querying the OPERA DSWx-HLS Catalog...\n",
+ "Found 15 scenes matching your query.\n",
+ "Removing objectid 6268553: no valid data inside AOI\n",
+ "Removing objectid 6268557: no valid data inside AOI\n",
+ "Removing objectid 6297588: no valid data inside AOI\n",
+ "12 scenes contain valid data within your bounding box.\n",
+ "3 scenes were excluded because all valid data falls outside the bounding box.\n",
+ "------------------------------------------------------------\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ " | | \n",
+ " objectid | \n",
+ " name | \n",
+ " datetime | \n",
+ " cloud_coverage | \n",
+ " tileid | \n",
+ " spacecraft_name | \n",
+ " downloadurl | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 6280533 | \n",
+ " OPERA_L3_DSWx-HLS_T12STF_20250104T182649Z_20250107T030638Z_S2B_30_v1.0_B01_WTR | \n",
+ " 2025-01-04 18:26:49 | \n",
+ " 0 | \n",
+ " T12STF | \n",
+ " Sentinel-2B | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 6280534 | \n",
+ " OPERA_L3_DSWx-HLS_T11SQA_20250104T182649Z_20250107T030637Z_S2B_30_v1.0_B01_WTR | \n",
+ " 2025-01-04 18:26:49 | \n",
+ " 0 | \n",
+ " T11SQA | \n",
+ " Sentinel-2B | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 6280536 | \n",
+ " OPERA_L3_DSWx-HLS_T11SPA_20250104T182649Z_20250107T030633Z_S2B_30_v1.0_B01_WTR | \n",
+ " 2025-01-04 18:26:49 | \n",
+ " 2 | \n",
+ " T11SPA | \n",
+ " Sentinel-2B | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 6280539 | \n",
+ " OPERA_L3_DSWx-HLS_T11SQV_20250104T182649Z_20250107T030643Z_S2B_30_v1.0_B01_WTR | \n",
+ " 2025-01-04 18:26:49 | \n",
+ " 10 | \n",
+ " T11SQV | \n",
+ " Sentinel-2B | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 6280541 | \n",
+ " OPERA_L3_DSWx-HLS_T11SPV_20250104T182649Z_20250107T030641Z_S2B_30_v1.0_B01_WTR | \n",
+ " 2025-01-04 18:26:49 | \n",
+ " 4 | \n",
+ " T11SPV | \n",
+ " Sentinel-2B | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 6302387 | \n",
+ " OPERA_L3_DSWx-HLS_T12STE_20250108T181539Z_20250113T211514Z_L8_30_v1.0_B01_WTR | \n",
+ " 2025-01-08 18:15:39 | \n",
+ " 0 | \n",
+ " T12STE | \n",
+ " Landsat-8 | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " 6308325 | \n",
+ " OPERA_L3_DSWx-HLS_T11SQA_20250109T182731Z_20250111T022843Z_S2A_30_v1.0_B01_WTR | \n",
+ " 2025-01-09 18:27:31 | \n",
+ " 0 | \n",
+ " T11SQA | \n",
+ " Sentinel-2A | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 7 | \n",
+ " 6308326 | \n",
+ " OPERA_L3_DSWx-HLS_T12STF_20250109T182731Z_20250111T022849Z_S2A_30_v1.0_B01_WTR | \n",
+ " 2025-01-09 18:27:31 | \n",
+ " 0 | \n",
+ " T12STF | \n",
+ " Sentinel-2A | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 8 | \n",
+ " 6308327 | \n",
+ " OPERA_L3_DSWx-HLS_T11SPA_20250109T182731Z_20250111T022845Z_S2A_30_v1.0_B01_WTR | \n",
+ " 2025-01-09 18:27:31 | \n",
+ " 2 | \n",
+ " T11SPA | \n",
+ " Sentinel-2A | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 9 | \n",
+ " 6308330 | \n",
+ " OPERA_L3_DSWx-HLS_T12STE_20250109T182731Z_20250111T022845Z_S2A_30_v1.0_B01_WTR | \n",
+ " 2025-01-09 18:27:31 | \n",
+ " 0 | \n",
+ " T12STE | \n",
+ " Sentinel-2A | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 10 | \n",
+ " 6308331 | \n",
+ " OPERA_L3_DSWx-HLS_T11SQV_20250109T182731Z_20250111T022841Z_S2A_30_v1.0_B01_WTR | \n",
+ " 2025-01-09 18:27:31 | \n",
+ " 0 | \n",
+ " T11SQV | \n",
+ " Sentinel-2A | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ " | 11 | \n",
+ " 6308332 | \n",
+ " OPERA_L3_DSWx-HLS_T11SPV_20250109T182731Z_20250111T022840Z_S2A_30_v1.0_B01_WTR | \n",
+ " 2025-01-09 18:27:31 | \n",
+ " 0 | \n",
+ " T11SPV | \n",
+ " Sentinel-2A | \n",
+ " Download Granule | \n",
+ "
\n",
+ " \n",
+ "
\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Set up the query parameters based on user configuration\n",
+ "query_params = {\n",
+ " # Spatial Parameters\n",
+ " 'geometry': BBOX, # The actual coordinates of your search area\n",
+ " 'geometryType': 'esriGeometryEnvelope', # Tells the server the coordinates for a rectangular Bounding Box\n",
+ " 'spatialRel': 'esriSpatialRelIntersects', # Returns any image that touches or overlaps the bounding box\n",
+ " 'inSR': '4326', # Specifies our coordinates are in standard Lat/Lon (WGS84)\n",
+ " \n",
+ " # Attribute Filter Parameters\n",
+ " 'where': f\"cloud_coverage <= {MAX_CLOUD_COVERAGE}\", # SQL-style filter to only keep scenes under our cloud limit\n",
+ " 'time': f\"{to_epoch_ms(START_DATE)},{to_epoch_ms(END_DATE)}\", # Time window converted to Epoch milliseconds\n",
+ " \n",
+ " # Output Formatting Parameters\n",
+ " 'outFields': FIELDS, # The specific fields/columns we want returned (in our table)\n",
+ " 'returnGeometry': 'false', # Do not return the polygon/footprint shapefiles/geometry\n",
+ " 'orderByFields': 'datetime ASC', # Sorts the final results chronologically (oldest to newest)\n",
+ " 'resultRecordCount': 2000 # Limits the server to returning 2000 records at once to prevent timeouts\n",
+ "}\n",
+ "\n",
+ "print(\"Querying the OPERA DSWx-HLS Catalog...\")\n",
+ "\n",
+ "# Execute the query\n",
+ "data = {\"features\": query_all_features(f\"{URL}/query\", query_params)}\n",
+ "\n",
+ "\n",
+ "# Parse the results into a Pandas DataFrame\n",
+ "if \"features\" in data and data[\"features\"]:\n",
+ " df = pd.DataFrame([f[\"attributes\"] for f in data[\"features\"]])\n",
+ "\n",
+ " if \"datetime\" in df.columns:\n",
+ " df[\"datetime\"] = pd.to_datetime(df[\"datetime\"], unit=\"ms\")\n",
+ " \n",
+ " total_scenes = len(df) \n",
+ " print(f\"Found {total_scenes} scenes matching your query.\")\n",
+ "\n",
+ " # The ImageServer catalog query only tells us which scene footprints intersect the AOI. \n",
+ " # Some of those scenes may contain no valid pixels inside the AOI.\n",
+ " # Request a small preview image for each scene and keep only scenes with\n",
+ " # at least one valid pixel within the bounding box.\n",
+ " valid_objectids = []\n",
+ " for _, row in df.iterrows():\n",
+ " objectid = row[\"objectid\"]\n",
+ "\n",
+ " # Tell the server to export exactly one scene -- no mosaicking.\n",
+ " # Without this mosaic rule, the ImageServer may combine or select among\n",
+ " # overlapping scenes according to the service's default mosaic behavior.\n",
+ " # Using esriMosaicLockRaster ensures that each exported image corresponds to \n",
+ " # exactly one ObjectID.\n",
+ " mosaic_rule = json.dumps({\n",
+ " \"mosaicMethod\": \"esriMosaicLockRaster\",\n",
+ " \"lockRasterIds\": [int(objectid)]\n",
+ " })\n",
+ "\n",
+ " img_params = {\n",
+ " \"bbox\": BBOX,\n",
+ " \"bboxSR\": \"4326\", # Input bounding box is in geographic (lat/lon) coordinates\n",
+ " \"imageSR\": \"3857\", # Export the image in Web Mercator projection to display correctly on the map\n",
+ " \"size\": \"256,256\",\n",
+ " \"format\": \"png32\",\n",
+ " \"transparent\": \"true\",\n",
+ " \"adjustAspectRatio\": \"false\", # Prevent ArcGIS from padding or changing the export extent to match the image size\n",
+ " \"mosaicRule\": mosaic_rule,\n",
+ " \"f\": \"image\"\n",
+ " }\n",
+ "\n",
+ " resp = requests.get(f\"{URL}/exportImage\", params=img_params, timeout=120)\n",
+ "\n",
+ " if (\n",
+ " resp.status_code == 200\n",
+ " and resp.headers.get(\"Content-Type\", \"\").startswith(\"image\")\n",
+ " and scene_has_valid_data(resp.content)\n",
+ " ):\n",
+ " valid_objectids.append(objectid)\n",
+ " else:\n",
+ " print(f\"Removing objectid {objectid}: no valid data inside AOI\")\n",
+ "\n",
+ " df = df[df[\"objectid\"].isin(valid_objectids)].reset_index(drop=True)\n",
+ " valid_scenes = len(df)\n",
+ "\n",
+ " print(f\"{valid_scenes} scenes contain valid data within your bounding box.\")\n",
+ " print(f\"{total_scenes - valid_scenes} scenes were excluded because all valid data falls outside the bounding box.\")\n",
+ " print(\"-\" * 60)\n",
+ "\n",
+ " if \"downloadurl\" in df.columns:\n",
+ " display(df.style.format({\"downloadurl\": make_clickable}))\n",
+ " else:\n",
+ " display(df)\n",
+ "\n",
+ "else:\n",
+ " print(\"No scenes found. Try expanding your date range, bounding box, or cloud coverage tolerance.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b3c31c39",
+ "metadata": {},
+ "source": [
+ "### Visualize Individual Scenes on an Interactive Map\n",
+ "\n",
+ "The following will allow you to view the individual scenes on an interactive map queried from the results above. Each scene can be toggled and viewed seperately."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "0ff957ce-6dcb-49a1-aeec-7271d6c87727",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Mapping 12 scenes...\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Base Map Setup\n",
+ "xmin, ymin, xmax, ymax = map(float, BBOX.split(\",\"))\n",
+ "\n",
+ "m = folium.Map(\n",
+ " location=[(ymin + ymax) / 2, (xmin + xmax) / 2],\n",
+ " zoom_start=8,\n",
+ " # tiles=\"OpenStreetMap\" # Standard street map basemap\n",
+ " tiles=\"Cartodb Positron\" # Light, low-clutter basemap that emphasizes the DSWx layers\n",
+ ")\n",
+ "\n",
+ "# OPTIONAL: Add a topographic tile layer\n",
+ "folium.TileLayer(\n",
+ " xyz.OpenTopoMap.url,\n",
+ " name='Topography',\n",
+ " attr=xyz.OpenTopoMap.html_attribution\n",
+ ").add_to(m)\n",
+ "\n",
+ "# AOI outline\n",
+ "folium.Rectangle(\n",
+ " bounds=[[ymin, xmin], [ymax, xmax]],\n",
+ " color=\"red\",\n",
+ " fill=False,\n",
+ " name=\"AOI\"\n",
+ ").add_to(m)\n",
+ "\n",
+ "# DSWx legend\n",
+ "legend_html = \"\"\"\n",
+ "\n",
+ "
DSWx-HLS Classes\n",
+ "
Not Water
\n",
+ "
Open Water
\n",
+ "
Partial Surface Water
\n",
+ "
HLS Snow/Ice
\n",
+ "
HLS Cloud/Cloud Shadow
\n",
+ "
\n",
+ "\"\"\"\n",
+ "\n",
+ "# Query the server for scene records that match your criteria.\n",
+ "# This does not download or export imagery. It returns metadata records that are \n",
+ "# used to identify which scenes should be exported and displayed on the map.\n",
+ "oid_params = {\n",
+ " \"geometry\": BBOX,\n",
+ " \"geometryType\": \"esriGeometryEnvelope\",\n",
+ " \"spatialRel\": \"esriSpatialRelIntersects\",\n",
+ " \"inSR\": \"4326\", # Input bounding box is in geographic (lat/lon) coordinates\n",
+ " \"returnGeometry\": \"false\",\n",
+ " \"where\": f\"cloud_coverage <= {MAX_CLOUD_COVERAGE}\",\n",
+ " \"time\": f\"{to_epoch_ms(START_DATE)},{to_epoch_ms(END_DATE)}\",\n",
+ " \"outFields\": \"objectid,name,datetime,spacecraft_name,cloud_coverage\",\n",
+ " \"orderByFields\": \"datetime ASC\"\n",
+ "}\n",
+ "\n",
+ "feats = query_all_features(f\"{URL}/query\", oid_params)\n",
+ "valid_oid_set = set(df[\"objectid\"])\n",
+ "feats = [f for f in feats if f[\"attributes\"][\"objectid\"] in valid_oid_set]\n",
+ "\n",
+ "print(f\"Mapping {len(feats)} scenes...\")\n",
+ "\n",
+ "# Optional rendering rule.\n",
+ "# Apply the service's predefined WTR rendering so exported images\n",
+ "# use the same class of colors shown in Earthdata Search.\n",
+ "rendering_rule = json.dumps({\n",
+ " \"rasterFunction\": \"OPERA_L3_DSWX-HLS_V1_B01_WTR\"\n",
+ "})\n",
+ "\n",
+ "# Use AOI bounds for every overlay so images align correctly\n",
+ "map_bounds = [[ymin, xmin], [ymax, xmax]]\n",
+ "\n",
+ "# Export each scene as a PNG image and read it into memory.\n",
+ "# The exported image is then added to the Folium map as an overlay.\n",
+ "# A separate export request is made for each scene because the\n",
+ "# mosaicRule changes for every objectid.\n",
+ "for feat in feats:\n",
+ " attr = feat[\"attributes\"]\n",
+ " objectid = attr[\"objectid\"]\n",
+ " dt_str = pd.to_datetime(attr[\"datetime\"], unit=\"ms\").strftime(\"%Y-%m-%d\")\n",
+ " craft = attr.get(\"spacecraft_name\", \"Unknown\")\n",
+ " cloud_pct = attr.get(\"cloud_coverage\", \"N/A\")\n",
+ " layer_name = f\"{dt_str} | {craft} | ☁ {cloud_pct}% | OID {objectid}\"\n",
+ "\n",
+ " # Tell the server to export exactly one scene -- no mosaicking.\n",
+ " # Without this mosaic rule, the ImageServer may combine or select among\n",
+ " # overlapping scenes according to the service's default mosaic behavior.\n",
+ " # Using esriMosaicLockRaster ensures that each exported image corresponds to \n",
+ " # exactly one ObjectID.\n",
+ " mosaic_rule = json.dumps({\n",
+ " \"mosaicMethod\": \"esriMosaicLockRaster\",\n",
+ " \"lockRasterIds\": [objectid]\n",
+ " })\n",
+ "\n",
+ " img_params = {\n",
+ " \"bbox\": BBOX,\n",
+ " \"bboxSR\": \"4326\", # Input bounding box is in geographic (lat/lon) coordinates\n",
+ " \"imageSR\": \"3857\", # Export the image in Web Mercator projection to display correctly on the map\n",
+ " \"size\": \"800,600\",\n",
+ " \"format\": \"png32\",\n",
+ " \"transparent\": \"true\",\n",
+ " \"adjustAspectRatio\": \"false\", # Prevent ArcGIS from padding or changing the export extent to match the image size\n",
+ " \"mosaicRule\": mosaic_rule,\n",
+ " \"renderingRule\": rendering_rule,\n",
+ " \"f\": \"image\"\n",
+ " }\n",
+ "\n",
+ " resp = requests.get(f\"{URL}/exportImage\", params=img_params, timeout=120)\n",
+ " if resp.status_code == 200 and resp.headers.get(\"Content-Type\", \"\").startswith(\"image\"):\n",
+ " b64_data = base64.b64encode(resp.content).decode(\"utf-8\")\n",
+ "\n",
+ " folium.raster_layers.ImageOverlay(\n",
+ " image=f\"data:image/png;base64,{b64_data}\",\n",
+ " bounds=map_bounds,\n",
+ " name=layer_name,\n",
+ " opacity=0.75,\n",
+ " show=False\n",
+ " ).add_to(m)\n",
+ " else:\n",
+ " print(f\"Could not map OID {objectid}: {resp.status_code}\")\n",
+ " print(resp.text[:300])\n",
+ "\n",
+ "m.get_root().html.add_child(folium.Element(legend_html)) #OPTIONAL: Add legend to map\n",
+ "folium.LayerControl(collapsed=False).add_to(m)\n",
+ "\n",
+ "display(m)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Geospatial_310",
+ "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.10.14"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/quarto_text/GIS.qmd b/quarto_text/GIS.qmd
index 96dd0438..3c052110 100644
--- a/quarto_text/GIS.qmd
+++ b/quarto_text/GIS.qmd
@@ -1,16 +1,35 @@
---
title: GIS
-subtitle: StoryMaps and Tutorials using Geographic Information Systems (GIS)
+subtitle: Tutorials for using NASA PO.DAAC data with Geographic Information Systems (GIS)
---
## **NASA Earthdata GIS (EGIS)**
+Select NASA Earthdata PO.DAAC collections are available through the [NASA Earthdata GIS (EGIS)](https://gis.earthdata.nasa.gov/portal/home/index.html), a resource for distributing cloud-native, GIS-ready NASA Earth Science data, services, and resources. These include ArcGIS and Open Geospatial Consortium (OGC)-compliant raster and feature geospatial services*. To access the full NASA Earth observation repository for data download, please visit [Earthdata Search](https://search.earthdata.nasa.gov/search).
+*Core functionality is available for EGIS web services; however, some unique behaviors persist. Please see our [Expected Behavior and Known Limitations](https://gis.earthdata.nasa.gov/portal/apps/storymaps/stories/67b9468490244f6998a19659eed1a1d9) for more details.
-- SWOT HR Level 2 River Single-Pass Vector Data Product GIS services available via [NASA Earhtdata GIS (EGIS)](https://gis.earthdata.nasa.gov/portal/home/index.html)
-- [SWOT Level 2 River Single-Pass Vector Data Product GIS services](../notebooks/GIS/SWOT_Rivers_EGIS_Services.md)
-- [SWOT Rivers EGIS Access Guide](../notebooks/GIS/SWOT_EGIS_access.ipynb)
-- [SWOT Rivers EGIS Python Guide](../notebooks/GIS/SWOT_EGIS_access_Python.ipynb)
+## **SWOT**
+SWOT EGIS Services - [Landing Page](https://gis.earthdata.nasa.gov/portal/home/group.html?id=8f40e6f75ec343c982af8047c470ed5f#overview)
-## **GIS StoryMaps of Select Datasets:**
+SWOT HR Level 2 River Single-Pass Vector Data Product EGIS resources:
+
+Services
+- SWOT L2 HR RiverSP - [EGIS Services Endpoints List](../notebooks/GIS/SWOT_Rivers_EGIS_Services.md)
+
+Tutorials
+- SWOT L2 HR RiverSP - [EGIS Access Guide](../notebooks/GIS/SWOT_EGIS_access.ipynb)
+- SWOT L2 HR RiverSP - [EGIS Python Guide](../notebooks/GIS/SWOT_EGIS_access_Python.ipynb)
+
+## **OPERA DSWx (Dynamic Surface Water Extent)**
+OPERA DSWx EGIS Services - Landing Page
+
+Services
+- OPERA L3 DSWX-HLS_V1 - [EGIS Services Endpoints List](../notebooks/GIS/OPERA_DSWx-HLS_EGIS_Services.md)
+
+Tutorials
+- OPERA L3 DSWX-HLS_V1 - [Access](../notebooks/GIS/OPERA_EGIS_access.ipynb)
+- OPERA L3 DSWX-HLS_V1 - [Access with Python](../notebooks/GIS/OPERA_EGIS_access_Python.ipynb)
+
+## **StoryMaps**
#### [**PO.DAAC GIS StoryMap Collection Page**](https://storymaps.arcgis.com/collections/413c17cba4b44be18e445c1caaf03fff)
@@ -19,13 +38,10 @@ subtitle: StoryMaps and Tutorials using Geographic Information Systems (GIS)
- [Utilizing GRACE data over the Colorado River Basin](https://storymaps.arcgis.com/collections/413c17cba4b44be18e445c1caaf03fff?item=3)
- [The Oceans & Melting Glaciers: OMG & GRACE](https://storymaps.arcgis.com/collections/413c17cba4b44be18e445c1caaf03fff?item=4)
-## **GIS Walkthroughs**
+## **Other GIS Tutorials**
- [Mapping Sea Surface Temperature Anomalies in QGIS](../notebooks/GIS/MUR_SSTA_QGIS.md)
-- [Transform SWOT Datetime field for use in GIS](../notebooks/GIS/SWOT_datetime_GIS.ipynb)
-
-## **GIS Jupyter Notebook Tutorials**
-
+- [Transform SWOT Datetime field using Py and ArcGIS/QGIS](../notebooks/GIS/SWOT_datetime_GIS.ipynb)
- [GIS SWOT shapefile exploration](../notebooks/GIS/SWOT_GISshapefiles.ipynb)
- NetCDF to Geotiff Conversion: SWOT Data Example - [mac or Linux](../notebooks/GIS/Subscriber_nc_to_tif_SWOT.qmd) | [Windows](../notebooks/GIS/GDAL_NetCDF_GeoTIFF.ipynb)
- OPERA DSWx-HLS access & mosaic visualization - [in the cloud](../notebooks/datasets/OPERA_GIS_Cloud.ipynb) | [locally](../notebooks/datasets/OPERA_GIS_Notebook.ipynb)