{ "cells": [ { "cell_type": "markdown", "id": "0d9243d6-3aaf-47ef-852b-d81174bf5e6e", "metadata": {}, "source": [ "# OPERA Surface Displacement from Sentinel-1: Access and Visualize" ] }, { "cell_type": "markdown", "id": "73064a66-73a5-4004-9731-65b4f023ff26", "metadata": {}, "source": [ "Authors: Harshini Girish (UAH), Rajat Shinde (UAH), Alex Mandel (Development Seed), Chuck Daniels (Development Seed), Julia Signell (Element84)\n", "\n", "Date:July 28, 2025\n", "\n", "Description: This tutorial aims to provide information and code to help users get started working with the OPERA Sentinel-1 Surface Displacement product using the MAAP. We will search for the data within NASA’s Common Metadata Repository (CMR)." ] }, { "cell_type": "markdown", "id": "a0ca2d41-6b47-4ff9-9405-ce0ed60dff18", "metadata": {}, "source": [ "## Run This Notebook" ] }, { "cell_type": "markdown", "id": "f61c7f88-f9a4-4f24-bb85-ec5222d42631", "metadata": {}, "source": [ "To access and run this tutorial within MAAP’s Algorithm Development Environment (ADE), please refer to the “Getting started with the MAAP” section of our documentation.\n", "\n", "Disclaimer: It is highly recommended that you run this tutorial within MAAP’s ADE, which already includes packages specific to MAAP, such as maap-py. Running the tutorial outside of the MAAP ADE may lead to errors.\n", "\n" ] }, { "cell_type": "markdown", "id": "f8d01bb3-368c-4eca-a4e3-73ce89fcd56c", "metadata": {}, "source": [ "## About the Data\n", "\n", "> The Level-3 OPERA Sentinel-1 Surface Displacement (DISP) product is generated through interferometric time-series analysis of Level-2 Coregistered Sentinel-1 Single Look Complex (CSLC) datasets. Using a hybrid Persistent Scatterer (PS) and Distributed Scatterer (DS) approach, this product quantifies Earth's surface displacement in the radar line-of-sight. The DISP products enable the detection of anthropogenic and natural surface changes, including subsidence, tectonic deformation, and landslides. \n", "\n", "> The OPERA DISP suite comprises complementary datasets derived from Sentinel-1 and NISAR inputs, designated as DISP-S1 and DISP-NI, respectively. Each product, created per acquisition, adheres to a consistent structure, HDF5 file format, file-naming convention, and a 30 m spatial posting. This collection specifically includes DISP-S1 products, derived from Sentinel-1 data. For visualization and quick exploration, the Pangeo Image can be used for these datasets. \n" ] }, { "cell_type": "markdown", "id": "dbb122dd-1079-465c-8348-8a97eb46130e", "metadata": {}, "source": [ "Source: [OPERA Surface Displacement from Sentinel-1](https://cmr.earthdata.nasa.gov/search/concepts/C3294057315-ASF.html)" ] }, { "cell_type": "markdown", "id": "7bae3cbe-f6f6-4591-8fcf-8d6f558bbc2e", "metadata": {}, "source": [ "## Importing Packages" ] }, { "cell_type": "code", "execution_count": 3, "id": "a0147000-e8a1-458f-acb0-b253960aabb7", "metadata": {}, "outputs": [], "source": [ "# --- MAAP & Cloud Access ---\n", "from maap.maap import MAAP\n", "import earthaccess\n", "from s3fs import S3FileSystem\n", "\n", "# File Access & Processing -\n", "import os\n", "import fsspec\n", "import h5py\n", "import re\n", "import numpy as np\n", "import xarray as xr\n", "import dask\n", "\n", "# Plotting & Visualization \n", "import matplotlib.pyplot as plt\n", "import folium\n", "\n", "# Geospatial \n", "import geopandas as gpd\n", "from shapely.geometry import box, Polygon\n", "\n", "# Misc\n", "import requests\n", "\n", "# Initialize MAAP\n", "maap = MAAP()\n" ] }, { "cell_type": "markdown", "id": "79bc1350-2cec-4a39-91ef-30f3def98514", "metadata": {}, "source": [ "## Searching the Data" ] }, { "cell_type": "markdown", "id": "9a84f593-66c9-4bba-9d84-4f376ba1ffa5", "metadata": {}, "source": [ "This performs a granule search using the `maap.searchGranule()` function on the OPERA Sentinel-1 displacement product collection." ] }, { "cell_type": "code", "execution_count": 16, "id": "ba83ee4e-75fd-4443-ae92-d6a356bee759", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "collection = maap.searchCollection(\n", " cmr_host=\"cmr.earthdata.nasa.gov\",\n", " short_name=\"OPERA_L3_DISP-S1_V1\"\n", ")\n", "len(collection)\n", "\n" ] }, { "cell_type": "code", "execution_count": 17, "id": "ebde15fe-125e-480f-883c-10bf86236494", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 100 granules with polygons.\n" ] } ], "source": [ "results = maap.searchGranule(\n", " short_name=\"OPERA_L3_DISP-S1_V1\",\n", " cmr_host=\"cmr.earthdata.nasa.gov\",\n", " limit=100\n", ")\n", "\n", "records = []\n", "for r in results:\n", " granule = r[\"Granule\"]\n", " try:\n", " points = granule[\"Spatial\"][\"HorizontalSpatialDomain\"][\"Geometry\"][\"GPolygon\"][\"Boundary\"][\"Point\"]\n", " coords = [(float(p[\"PointLongitude\"]), float(p[\"PointLatitude\"])) for p in points]\n", " if len(coords) >= 3:\n", " polygon = Polygon(coords)\n", " records.append({\n", " \"GranuleUR\": granule[\"GranuleUR\"],\n", " \"geometry\": polygon\n", " })\n", " except KeyError:\n", " continue\n", "if records:\n", " gdf = gpd.GeoDataFrame(records, crs=\"EPSG:4326\")\n", " print(f\"Found {len(gdf)} granules with polygons.\")\n", "\n", " " ] }, { "cell_type": "markdown", "id": "ba0a22e8-aab6-4043-a409-086240addd65", "metadata": {}, "source": [ "## Visualizing with Bounding Boxes" ] }, { "cell_type": "markdown", "id": "5ae9d96a-3ff6-42ba-940c-8fe2334fd575", "metadata": {}, "source": [ "This code creates an interactive map showing bounding boxes for each granule using `folium`. It extracts geometry bounds from a `GeoDataFrame`, constructs a new `GeoDataFrame` of bounding boxes, and overlays them on a Leaflet map with tooltips displaying each granule's ID.\n" ] }, { "cell_type": "code", "execution_count": 18, "id": "ea2ac1d2-ee56-4a75-be51-28ee6445600d", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bounding_boxes = []\n", "granule_ids = []\n", "\n", "for i, geom in enumerate(gdf.geometry):\n", " minx, miny, maxx, maxy = geom.bounds\n", " bounding_boxes.append(box(minx, miny, maxx, maxy)) \n", " granule_ids.append(gdf[\"GranuleUR\"][i])\n", "\n", "\n", "bbox_gdf = gpd.GeoDataFrame({\n", " \"GranuleUR\": granule_ids,\n", " \"geometry\": bounding_boxes\n", "}, crs=\"EPSG:4326\")\n", "\n", "\n", "map_center = bbox_gdf.geometry.union_all().centroid\n", "m = folium.Map(location=[map_center.y, map_center.x], zoom_start=6)\n", "\n", "\n", "for _, row in bbox_gdf.iterrows():\n", " folium.GeoJson(\n", " row[\"geometry\"],\n", " style_function=lambda x: {\n", " \"color\": \"green\",\n", " \"weight\": 2,\n", " \"fillOpacity\": 0.1\n", " },\n", " tooltip=row[\"GranuleUR\"]\n", " ).add_to(m)\n", "\n", "m\n" ] }, { "cell_type": "markdown", "id": "e6419706-d855-47b1-8afa-ccae9e5b1963", "metadata": {}, "source": [ "## Search Granules using filters" ] }, { "cell_type": "markdown", "id": "174b6b81-247c-4f34-9248-bda6bf85fc13", "metadata": {}, "source": [ "### Temporal Filter" ] }, { "cell_type": "markdown", "id": "90caadc4-e4a0-4d05-87b7-e048c3be7c86", "metadata": {}, "source": [ "Now that we have our collection ID, let’s search for granules within the collection. We’ll also add a temporal filter to our search. If you would like to search for granules without the temporal filter, simply comment out or remove the `temporal=date_range` line." ] }, { "cell_type": "code", "execution_count": 19, "id": "a37781b9-c735-47e9-a136-0554957aac57", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 20 granules\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20160818T005558Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170322T005556Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20160911T005559Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170226T005556Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20171012T005606Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170509T005558Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20160725T005557Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170403T005556Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170918T005605Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170602T005559Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20171024T005606Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170930T005605Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170310T005556Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40286_VV_20160701T005555Z_20170415T005557Z_v1.0_20250724T212204Z\n", "OPERA_L3_DISP-S1_IW_F40287_VV_20160701T005608Z_20161005T005612Z_v1.0_20250724T213133Z\n", "OPERA_L3_DISP-S1_IW_F40287_VV_20160701T005608Z_20170202T005609Z_v1.0_20250724T213133Z\n", "OPERA_L3_DISP-S1_IW_F40287_VV_20160701T005608Z_20161122T005612Z_v1.0_20250724T213133Z\n", "OPERA_L3_DISP-S1_IW_F40287_VV_20160701T005608Z_20161029T005613Z_v1.0_20250724T213133Z\n", "OPERA_L3_DISP-S1_IW_F40287_VV_20160701T005608Z_20170214T005609Z_v1.0_20250724T213133Z\n", "OPERA_L3_DISP-S1_IW_F40287_VV_20160701T005608Z_20170109T005609Z_v1.0_20250724T213133Z\n" ] } ], "source": [ "date_range = \"2016-07-01T00:00:00Z,2016-07-25T23:59:59Z\"\n", "concept_id = collection[0][\"concept-id\"]\n", "\n", "results = maap.searchGranule(\n", " temporal=date_range,\n", " concept_id=concept_id,\n", " cmr_host=\"cmr.earthdata.nasa.gov\"\n", ")\n", "\n", "print(f\"Found {len(results)} granules\")\n", "for r in results:\n", " print(r[\"Granule\"][\"GranuleUR\"])\n" ] }, { "cell_type": "markdown", "id": "09db23fd-6026-495a-ab94-b328781065ca", "metadata": {}, "source": [ "### Spatial Filter" ] }, { "cell_type": "markdown", "id": "a40ec4bc-c484-4286-abc1-11a1ad9c0fa1", "metadata": {}, "source": [ "Another filter we can apply is a spatial filter." ] }, { "cell_type": "code", "execution_count": 20, "id": "a12e3962-9f67-491b-a0db-2b1c7c3249a9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 20 granules\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20161029T005740Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20170310T005736Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20161005T005740Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20160725T005737Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20170403T005737Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20161216T005739Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20170214T005736Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20170322T005736Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20161122T005739Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20170226T005736Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20160818T005738Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20170109T005737Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20160911T005739Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40291_VV_20160701T005736Z_20170202T005736Z_v1.0_20250724T213304Z\n", "OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20160725T005759Z_v1.0_20250412T124848Z\n", "OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20170109T005759Z_v1.0_20250412T124848Z\n", "OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20160818T005800Z_v1.0_20250412T124848Z\n", "OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20161029T005802Z_v1.0_20250412T124848Z\n", "OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20170310T005758Z_v1.0_20250412T124848Z\n", "OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20170403T005759Z_v1.0_20250412T124848Z\n" ] } ], "source": [ "granule_bbox = \"-104.57446,23.91956,-101.85669,25.95518\" \n", "\n", "results = maap.searchGranule(\n", " concept_id=concept_id,\n", " bounding_box=granule_bbox,\n", " cmr_host=\"cmr.earthdata.nasa.gov\"\n", ")\n", "\n", "print(f\"Found {len(results)} granules\")\n", "for r in results:\n", " print(r[\"Granule\"][\"GranuleUR\"])\n" ] }, { "cell_type": "markdown", "id": "4b92ff97-d27d-4eae-b2f8-5643c8472468", "metadata": {}, "source": [ "## Locally download and Inspect " ] }, { "cell_type": "markdown", "id": "d35a578b-0556-4923-bb8b-51a35eb2440b", "metadata": {}, "source": [ "This code snippet queries NASA’s CMR via the MAAP API to fetch a granule from the `OPERA_L3_DISP-S1_V1` collection and downloads it locally into a folder called `opera_data`. It then uses `xarray.open_dataset()` with the `h5netcdf` engine to open the local NetCDF file and inspects its structure, including its shape, coordinates, and variable metadata.\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "96735105-a00e-43f0-86bf-cb622a971b4a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Size: 4GB\n", "Dimensions: (y: 7915, x: 9548, time: 1)\n", "Coordinates:\n", " * y (y) float64 63kB 1.995e+06 ... 1.758e+06\n", " * x (x) float64 76kB 7.682e+04 ... 3.632e+05\n", " * time (time) datetime64[ns] 8B 2016-08-18T00:55...\n", "Data variables: (12/13)\n", " spatial_ref int64 8B ...\n", " reference_time (time) datetime64[ns] 8B ...\n", " displacement (y, x) float64 605MB ...\n", " short_wavelength_displacement (y, x) float32 302MB ...\n", " recommended_mask (y, x) float32 302MB ...\n", " connected_component_labels (y, x) float32 302MB ...\n", " ... ...\n", " estimated_phase_quality (y, x) float32 302MB ...\n", " persistent_scatterer_mask (y, x) float32 302MB ...\n", " shp_counts (y, x) float32 302MB ...\n", " water_mask (y, x) float32 302MB ...\n", " phase_similarity (y, x) float32 302MB ...\n", " timeseries_inversion_residuals (y, x) float32 302MB ...\n", "Attributes:\n", " Conventions: CF-1.8\n", " contact: opera-sds-ops@jpl.nasa.gov\n", " institution: NASA JPL\n", " mission_name: OPERA\n", " reference_document: JPL D-108765\n", " title: OPERA_L3_DISP-S1 Product\n" ] } ], "source": [ "results = maap.searchGranule(\n", " short_name=\"OPERA_L3_DISP-S1_V1\",\n", " cmr_host=\"cmr.earthdata.nasa.gov\"\n", ")\n", "\n", "\n", "data_dir = \"opera_data\"\n", "os.makedirs(data_dir, exist_ok=True)\n", "\n", "\n", "file_path = results[0].getData(data_dir)\n", "\n", "ds = xr.open_dataset(file_path, engine=\"h5netcdf\")\n", "print(ds)\n" ] }, { "cell_type": "markdown", "id": "289b80d3-708a-4634-96c7-233f46ce8ce3", "metadata": {}, "source": [ "## Visualization" ] }, { "cell_type": "markdown", "id": "309c6e5f-a29d-4167-bc37-0abd93d3a3bd", "metadata": {}, "source": [ "This plot visualizes the radar Line-of-Sight (LOS) displacement from an OPERA DISP-S1 granule. The displacement field is displayed with a diverging colormap, highlighting motion towards and away from the sensor.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ec802035-78b9-4c21-af46-f0e2e5ad1e3d", "metadata": {}, "outputs": [], "source": [ "ds[\"displacement\"].attrs\n" ] }, { "cell_type": "code", "execution_count": null, "id": "189e79e5-644a-493b-baaa-d94c68de49e0", "metadata": {}, "outputs": [], "source": [ "gname = results[0][\"Granule\"][\"GranuleUR\"]\n", "t = re.search(r'_(\\d{8}T\\d{6}Z)_(\\d{8}T\\d{6}Z)', gname)\n", "ts = f\"{t.group(1)} to {t.group(2)}\" if t else \"Time unknown\"\n", "\n", "ds[\"displacement\"].squeeze().plot(\n", " cmap=\"RdBu\", vmin=-0.1, vmax=0.1, figsize=(8, 6),\n", " cbar_kwargs={\"label\": \"Line-of-sight displacement [m]\"}\n", ")\n", "plt.title(f\"LOS displacement from {ts}\")\n", "plt.xlabel(\"X coord [m]\"); plt.ylabel(\"Y coord [m]\")\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "id": "bbb78c7d-d548-4bde-837f-9a67969c6ec1", "metadata": {}, "source": [ "## Cloud Optimized Remote Access\n" ] }, { "cell_type": "markdown", "id": "02b1fd01-d8a8-40b4-8962-711a1700b569", "metadata": {}, "source": [ "This setup enables efficient streaming of large NetCDF files from NASA’s cloud using `earthaccess` and `fsspec`. By specifying `blockcache` and tuning HDF5 driver settings like `page_buf_size` and `rdcc_nbytes`, it optimizes chunked reads. The dataset is opened in-memory with `xr.open_dataset()` using `h5netcdf`, without without pre-downloading the entire file.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "936e03bf-d62e-4b96-9776-fc45ee73562c", "metadata": {}, "outputs": [], "source": [ "auth = earthaccess.login()\n", "\n", "granules = earthaccess.search_data(\n", " count=1,\n", " short_name=\"OPERA_L3_DISP-S1_V1\"\n", ")\n", "granule = granules[0]\n", "url = granule.data_links(access=\"direct\")[0]\n", "\n", "credentials = auth.get_s3_credentials(\n", " endpoint=granule.get_s3_credentials_endpoint()\n", ")\n", "\n", "s3 = S3FileSystem(\n", " key=credentials['accessKeyId'],\n", " secret=credentials['secretAccessKey'],\n", " token=credentials['sessionToken']\n", ")\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "4d6bd309-ad76-4b2c-b181-928ae7d07ca5", "metadata": {}, "outputs": [], "source": [ "io_params = {\n", " \"fsspec_params\": {\n", " \"cache_type\": \"blockcache\",\n", " \"block_size\": 8 * 1024 * 1024\n", " },\n", " \"h5py_params\": {\n", " \"driver_kwds\": {\n", " \"page_buf_size\": 16 * 1024 * 1024,\n", " \"rdcc_nbytes\": 4 * 1024 * 1024\n", " }\n", " }\n", "}\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "1f69ac7f-c565-458d-9054-7211f313a750", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.Dataset> Size: 4GB\n",
       "Dimensions:                         (y: 7915, x: 9548, time: 1)\n",
       "Coordinates:\n",
       "  * y                               (y) float64 63kB 1.995e+06 ... 1.758e+06\n",
       "  * x                               (x) float64 76kB 7.682e+04 ... 3.632e+05\n",
       "  * time                            (time) datetime64[ns] 8B 2016-08-18T00:55...\n",
       "Data variables: (12/13)\n",
       "    spatial_ref                     int64 8B ...\n",
       "    reference_time                  (time) datetime64[ns] 8B dask.array<chunksize=(1,), meta=np.ndarray>\n",
       "    displacement                    (y, x) float64 605MB dask.array<chunksize=(4096, 4096), meta=np.ndarray>\n",
       "    short_wavelength_displacement   (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "    recommended_mask                (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "    connected_component_labels      (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "    ...                              ...\n",
       "    estimated_phase_quality         (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "    persistent_scatterer_mask       (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "    shp_counts                      (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "    water_mask                      (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "    phase_similarity                (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "    timeseries_inversion_residuals  (y, x) float32 302MB dask.array<chunksize=(5632, 5632), meta=np.ndarray>\n",
       "Attributes:\n",
       "    Conventions:         CF-1.8\n",
       "    contact:             opera-sds-ops@jpl.nasa.gov\n",
       "    institution:         NASA JPL\n",
       "    mission_name:        OPERA\n",
       "    reference_document:  JPL D-108765\n",
       "    title:               OPERA_L3_DISP-S1 Product
" ], "text/plain": [ " Size: 4GB\n", "Dimensions: (y: 7915, x: 9548, time: 1)\n", "Coordinates:\n", " * y (y) float64 63kB 1.995e+06 ... 1.758e+06\n", " * x (x) float64 76kB 7.682e+04 ... 3.632e+05\n", " * time (time) datetime64[ns] 8B 2016-08-18T00:55...\n", "Data variables: (12/13)\n", " spatial_ref int64 8B ...\n", " reference_time (time) datetime64[ns] 8B dask.array\n", " displacement (y, x) float64 605MB dask.array\n", " short_wavelength_displacement (y, x) float32 302MB dask.array\n", " recommended_mask (y, x) float32 302MB dask.array\n", " connected_component_labels (y, x) float32 302MB dask.array\n", " ... ...\n", " estimated_phase_quality (y, x) float32 302MB dask.array\n", " persistent_scatterer_mask (y, x) float32 302MB dask.array\n", " shp_counts (y, x) float32 302MB dask.array\n", " water_mask (y, x) float32 302MB dask.array\n", " phase_similarity (y, x) float32 302MB dask.array\n", " timeseries_inversion_residuals (y, x) float32 302MB dask.array\n", "Attributes:\n", " Conventions: CF-1.8\n", " contact: opera-sds-ops@jpl.nasa.gov\n", " institution: NASA JPL\n", " mission_name: OPERA\n", " reference_document: JPL D-108765\n", " title: OPERA_L3_DISP-S1 Product" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ds = xr.open_dataset(\n", " s3.open(url, \"rb\", **io_params[\"fsspec_params\"]),\n", " engine=\"h5netcdf\",\n", " chunks=\"auto\", \n", " **io_params[\"h5py_params\"]\n", ")\n", "ds" ] }, { "cell_type": "markdown", "id": "b4e70ebb-da75-48bb-8580-3e1fb405383c", "metadata": {}, "source": [ "Each data variable (e.g., `displacement`, `temporal_coherence`) is internally chunked into blocks (e.g., (5632, 2283), (5632, 3916), or (4096, 3819)). This chunking enables partial reads via HTTP range requests using `fsspec`, allowing efficient, on-demand access without loading the full file into memory — ideal for cloud-based workflows.\n" ] }, { "cell_type": "markdown", "id": "1818a9a4-58b3-472a-ac1b-feda2444369a", "metadata": {}, "source": [ "## Cloud-Optimized Performance" ] }, { "cell_type": "markdown", "id": "2efec3fb-2a8f-421e-aa01-b00706604a8a", "metadata": {}, "source": [ "This code uses `isel()` to select a small index-based window from the `displacement` variable. It applies `.compute()` to load only the required chunks with Dask. After filtering out NaNs, it plots a histogram of valid displacement values." ] }, { "cell_type": "code", "execution_count": 8, "id": "5a1c0bbe-0413-43d4-ac40-eaad2aa80543", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 5.28 ms, sys: 4.11 ms, total: 9.4 ms\n", "Wall time: 8.99 ms\n" ] } ], "source": [ "%time subset = ds['displacement'].isel(y=slice(230, 250), x=slice(8390, 8410)).compute()\n" ] }, { "cell_type": "code", "execution_count": 9, "id": "2fc262fa-2f77-4cb8-888f-0293df7dbdc8", "metadata": {}, "outputs": [ { "data": { "image/png": "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DlafPn105swZh3jOnTunOXPm2P8tZPX6Tp06VSEhIWrdunWWz3NYWJg6duyo1atXq379+vL391e1atX04YcfOtXdsmWLmjZtKj8/P5UvX15RUVG6ePGiy34XL16siIgIFS1aVMWKFVObNm20a9cu+/5NmzbJ29tbw4YNc2iX/l6bOXNmlrG74ufnJx8fH6cfPkeNGqVGjRqpZMmSCgwMVP369TVz5kwZYxzqrV+/Xi1btlSpUqXk7++vSpUqqXPnzvrnn3/sdS5cuKDXXnvN/hlUpkwZPfroo/rjjz8c+rp48aKGDx+ukJAQFSlSRLfffru2bt2arfM4deqUJGU4OtHD43+pjd69eztNm5b+9x5zJat/k//884/934ufn59Kliyphg0bauHChQ71tm/frnvuuUclS5aUn5+f6tWrp48//ti+f/bs2erataskqVWrVvb38OzZs+11evTooXXr1umXX37J+AkpTAyQj9SsWdM0atQoW3UlmZEjRzqVh4aGml69etkfz5o1y0gya9euNb179zYlSpQwRYoUMR07djS//PKLQ9udO3eaDh06mDJlyhgfHx9Trlw50759e3P06FF7nbS0NPPee++ZOnXqGD8/P1O8eHHTuXNnh75atGhhJDltmWnRooWpWbNmhvvbt29vbDabOXz4cIbnmpqaal599VVz8803Gz8/PxMUFGRq165tYmJi7HVGjhxpJJmdO3eaTp06mYCAABMYGGgefvhhc+LECaeYWrRo4VAWHR1tbrvtNlOiRAkTEBBg6tWrZz744AOTlpbmFPP8+fNN48aNTdGiRU3RokVNnTp1zAcffOBQJzY21txxxx0mICDA+Pv7myZNmph169Y51EmPec+ePaZLly4mMDDQlChRwgwZMsRcvHjR/Pjjj6ZNmzamWLFiJjQ01IwfP94pljNnzphnn33WhIWFGW9vb1O+fHkzaNAgc/bsWYd6ksyTTz5p5s6da6pVq2b8/f3NLbfcYj777DOneK7eNmzY4HTcq9ts3rzZSDKrV6+27ztw4ICRZGJjY03RokUdXtN///3XDB061NSpU8d+3o0bNzYrVqxwOkZ67NOmTTM33XST8fHxMdWrVzcLFy7MMC4AwPVJv87YsmWLuXjxotPWvn17Exoa6tDm6u/vsWPHGk9PTzNy5Ejz1VdfmdWrV5uYmBgTHR1tjDHm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8wVNwbh9Bv5z0j9IpcsL4j3//PN66623NGHCBE2YMOGa+szowyb9gyb9YuL111/XCy+8oL/++kvlypVT//799dJLL8nb21u///67jDEZXrxVrlz5mmLLjvSESGYXRFFRUSpatKjmzZunadOmydPTU82bN9f48eMd7sIhZfyFkNnFUPoXWMuWLTVjxgzdcMMN8vHx0YoVK/T666/bPzjTFzXM7ILoyouUjPz5558qWrSo/bGrC58iRYo4XWT4+Pg4JOR+//13/fzzzxne2c7VxcDVrr4QuR5FixZVt27d9OGHHyo0NFR33XWXQkNDXdZdtmyZHnjgAXXt2lXPPfecQkJC5OXlpalTp7pccDOrL9WsLlIBAHnPy8tLQ4cO1dChQ3X69GmtW7dOL7zwgtq0aaOjR4+qSJEiGbZN/85KTEx0+oz/7bffVLp0aYd6Vy50nS4pKcnlosCuFgSeN2+eWrZsqalTpzqU//3335mfpAWuPNer/fbbb/Lw8FCJEiUsP+7WrVuVlJSkvn37Zlqvdu3aWrRokYwx2rt3r2bPnq3Ro0fL399fI0aMsNdLSkpyapteltEf52fOnNHnn3+ukSNHOvSVkpLilNQpU6ZMlj9ElS5dWv7+/i6vJdL3WyG9n3feecfhzoZXyui62irpMVz9PF2vUqVKZfpaXn38JUuWZHi9d6XY2FhNnTpVt912m5YvX66lS5eqc+fO9v3R0dF66qmn7I+v/CE1I7fccovWrFmjn376SbfddpsWLVokb29vff755w7X0StWrHBq26xZMzVr1kypqanavn273nnnHQ0ePFjBwcF68MEHVbp0aZUqVSrDOxumx5f+3k5KSlKFChXs+y9dupTp3x9Zad68uSIjI7VixQqdOHFCZcuWlZ+fn1JSUpzqZpQUys6/yaJFi2rUqFEaNWqUfv/9d3355ZcaMWKE7r77bv3444/21zkqKkr333+/y+NUrVo12+f1559/uvxcLoxISiHf8/b21siRI/XWW29p37599nJfX1+XHzYZfahl9GFz44032h9ndTFRunRp2Ww2ffPNN/Zfba7kqswqK1eulM1mU/PmzTOsk5OL2oy+EDL7pSK7X2Dpt0g+duyYKlas6LKvvLxIyasLr5zo06ePPvjgA+3du1fz58/PsN68efMUHh6uxYsXO/xx4Oq9L13bhS4AIP8oXry4unTpouPHj2vw4ME6fPiwatSokeFokfRbhs+bN0+33nqrvXzbtm3av3+/feRDo0aN5Ovrq8WLFzv8wbRlyxYdOXIk23/82Gw2p+udvXv3Kj4+PsPvfKtUrVpVFSpU0IIFCzRs2DD79+K5c+e0dOlS+x35rPTnn39qwIAB8vb21pAhQ7LVxmazqU6dOnrrrbc0e/Zs7dy502H/V199pd9//91+nZOamqrFixerSpUqGf54ZLPZZIxxeu4/+OADpaamOpS1a9dOH330kQ4cOJDhH8EdO3bUmDFjVKpUKYWHh2frvK5F06ZNVbx4cf3www8OiZTrlZMfC0NDQ+Xv72/5ncxatWqllStXunwtr9SmTRt5eXnpl19+cUguuZKYmKhHHnlELVq0UGxsrO6//3717dtX9evXt79OYWFhOU5W7N69W9L/rtFtNpu8vLzk6elpr/Pvv//qo48+yrAPT09PNWrUSNWqVdP8+fO1c+dOPfjgg+rYsaMWLVqk1NRUNWrUKMP26XdInD9/vho0aGAv//jjj3Xp0qUsz+H3339XmTJlHO6yJ11+zg8ePKgiRYqoePHiki4/RydOnHB4bS5cuKA1a9a47Dun/yaDg4PVu3dv7dmzRzExMfrnn39UtWpV3XTTTdqzZ4/GjBmT6blkNfrv0qVLOnr0qNq3b59pP4UFSSnkK4mJiS6HZKdPU7pylFBYWJj27t3rUG/9+vU6e/asy77nz5/v8EWwefNmHTlyRP369XOqm9HFRMeOHTVu3DgdP35cDzzwQKbnYuXImlmzZunLL79U9+7dValSpWy1yeiiNl1GXwiZ3VI3u19gkZGR8vT01NSpUxUREeGyr9y6SHElNy68rncoeUREhP0Wx506dcqwns1mk4+Pj0NCKikpSZ9++qnL+tdyoQsAcK+7775btWrVUsOGDVWmTBkdOXJEMTExCg0N1U033SRJ9ik8b7/9tnr16iVvb29VrVpVVatW1WOPPaZ33nlHHh4eateunQ4fPqyXX35ZFStWtCdSSpYsqaFDh2rs2LEqUaKEOnXqpGPHjmnUqFEqV66c0x96GenYsaNeffVVjRw5Ui1atNCBAwc0evRohYeHZ+sPy+vh4eGhCRMm6OGHH1bHjh31+OOPKyUlRW+88YZOnz6tcePGXVf/Bw8e1JYtW5SWlqZTp07pu+++08yZM5WcnKy5c+eqZs2aGbb9/PPPNWXKFN13332qXLmyjDFatmyZTp8+rdatWzvULV26tO644w69/PLLKlq0qKZMmaIff/zR6Rb0VwoMDFTz5s31xhtvqHTp0goLC1NcXJxmzpxp/0M83ejRo/Xll1+qefPmeuGFF1S7dm2dPn1aq1ev1tChQ1WtWjUNHjxYS5cuVfPmzTVkyBDdcsstSktLU0JCgtauXatnn3020wRDdhUrVkzvvPOOevXqpT///FNdunRR2bJl9ccff2jPnj36448/nEbdZUft2rW1ceNGffbZZypXrpwCAgIyTMD5+PgoIiJCW7Zsud7TcfDSSy9p5cqVuuOOO/TKK6+oSJEieu+995yWnwgLC9Po0aP14osv6tdff1Xbtm1VokQJ/f7779q6dat9BE5qaqoeeugh2Ww2LViwQJ6enpo9e7bq1q2rbt26adOmTfbpkZnZt2+f/d/iqVOntGzZMsXGxqpTp0726+AOHTpo0qRJ6t69ux577DGdOnVKEydOdEp6Tps2TevXr1eHDh1UqVIlnT9/3v4j71133SVJevDBBzV//ny1b99egwYN0m233SZvb28dO3ZMGzZs0L333qtOnTqpevXqeuSRRxQTEyNvb2/ddddd2rdvnyZOnKjAwMAsz+ujjz7S+++/r+7du+vWW29VUFCQjh07pg8++EDff/+9XnnlFfvz061bN73yyit68MEH9dxzz+n8+fOaPHmyUwI3XXb+TTZq1EgdO3bULbfcohIlSmj//v366KOPHJLh77//vtq1a6c2bdqod+/eqlChgv7880/t379fO3fu1CeffCJJqlWrliRp+vTpCggIkJ+fn8LDw+0/IO/du1f//POPWrVqleXzUii4bzkrwFnt2rVNu3btzJQpU8z69evNunXrzMSJE025cuVMsWLFHBb6e+2114zNZjMvv/yyWbdunZk8ebK5+eabTVBQUIZ33+vbt6/9Fshly5Y1FSpUMKdOnTLGGPPZZ5+Zdu3amffff9/ExsaatWvXmgEDBhhJZvr06fb+HnvsMVOkSBHz3HPPmc8++8ysX7/ezJ8/3zzxxBMOd7ZIXxxvypQp5rvvvnNaZPtqLVq0MJUrV7bfyW39+vXmgw8+MB07drTfFefKxbeNcb3Q+YgRI8ySJUtMXFycmTt3rgkLCzOhoaH2u+pdffe9tWvXmrfeessU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vmfFhoLr0KFD7g4BAAAgW0hKAQAAZENCQoLLkdvGGIelBNztxhtvVKtWrTRv3jydP3/e3eEAAABkiKQUAACFhIdslm1wFh4erj/++MOp/M8//1R4eLgbInJtz549qlevnp599lmFhITo8ccf19atW90dFgAAgBOSUgAAFBZWTd0jJ+WSMUY2F3Mbz549Kz8/PzdE5FqtWrU0adIkHT9+XLNmzVJSUpJuv/121axZU5MmTXKZWAMAAHCHArHQOQAAgLsMHTpUkmSz2fTyyy+rSJEi9n2pqan67rvvVLduXTdFlzEvLy916tRJ7du315QpUxQVFaVhw4YpKipK3bp10/jx41WuXDl3hwkAAP7DSEoBAFBIcPe93LFr1y5Jl0dK/d///Z98fHzs+3x8fFSnTh0NGzbMXeFlaPv27frwww+1aNEiFS1aVMOGDVPfvn3122+/6ZVXXtG9997LtD4AAOBWJKUAAAAysWHDBknSo48+qrfffluBgYFujihzkyZN0qxZs3TgwAG1b99ec+fOVfv27eXhcXnVhvDwcL3//vuqVq2amyMFAAD/dSSlAAAoJKxaDoqBUq7NmjXL3SFky9SpU9WnTx89+uijCgkJcVmnUqVKmjlzZh5HBgAA4IikFAAAhYSHzSYPFwtxX0s/cHbu3DmNGzdOX331lU6cOKG0tDSH/b/++qubInN08ODBLOv4+PioV69eeRANAABAxnKclPr333/l7+/vcl9iYiILZgIAgEKpX79+iouLU48ePVSuXDmXd+LLD2bNmqVixYqpa9euDuWffPKJ/vnnH5JRAAAg38hxUqpevXpasGCB6tev71C+ZMkSPfHEE9xmGAAAN7FJsiJPkj9TLe735Zdf6osvvlDTpk3dHUqmxo0bp2nTpjmVly1bVo899hhJKQAAkG945LRB69at1aRJE40bN07GGJ09e1a9e/dWr1699Morr+RGjAAAIBs8LNzgrESJEipZsqS7w8jSkSNHFB4e7lQeGhqqhIQEN0QEAADgWo6vO9955x2tWLFCb7/9tpo3b646depoz5492rZtm55++unciBEAAMDtXn31Vb3yyiv6559/3B1KpsqWLau9e/c6le/Zs0elSpVyQ0QAAACuXdNC55GRkbr//vs1depUeXl56bPPPlONGjWsjg34z0v95TtrO7yQYml3JqCMpf1JktcN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vHixBg8erBdffFG7du1Ss2bN1K5dOyUkJLisf+jQIbVv317NmjXTrl279MILL+iZZ55xmPoVHx+vbt26qUePHtqzZ4969OihBx54QN999901H/d6DBs2TAcOHFCVKlV099136/7773fY8ovWrVsrJibG/thms+ns2bMaOXKk2rdvb9lxbr31Vi1fvlwLFy5UrVq19OqrryomJkYPP/ywZccAAACFW46TUhUrVnT5S+b777/PugAAAPxHTZo0SX379lW/fv1UvXp1xcTEqGLFipo6darL+tOmTVOlSpUUExOj6tWrq1+/furTp48mTpxorxMTE6PWrVsrKipK1apVU1RUlO68806HhEtOj3s9nn76aW3YsEE333yzSpUqpaCgIIctv3jrrbcUFxenGjVq6Pz58+revbvCwsJ0/PhxjR8/3tJjdezYUf/3f/+n8+fPa//+/erfv7+l/QMAgMItW2tKXemtt95S586dtWbNGjVu3FiStGXLFv3yyy8ZLmwJAAByn812ebOin5y4cOGCduzYoREjRjiUR0ZGavPmzS7bxMfHKzIy0qGsTZs2mjlzpi5evChvb2/Fx8dryJAhTnXSk1LXctzrMXfuXC1dulQdOuTvOxCVL19eu3fv1sKFC7Vz506lpaWpb9++evjhh+Xv7+/u8AAAAOxynJRq3769Dh48qClTpujHH3+UMUb33nuvBgwYwEipPJTy9ULL+7TVamVpfxdj8/caYx5FrFtXI53PzfWs7dDbJ+s6OWCzuD/9/Ye1/UlK+Xq3pf35Nn/I0v6A/5Lk5GSHxxktVH3y5EmlpqYqODjYoTw4OFhJSUku+05KSnJZ/9KlSzp58qTKlSuXYZ30Pq/luNejZMmSqlKliuX95gZ/f3/16dNHffr0cXcoAAAAGcpxUkqSbrjhBo0ZM8bqWAAAwHWwGSObMZb0I8npx6aRI0cqOjo643ZXDbEyxjiVZVX/6vLs9JnT416r6OhojRw5UrNmzVKRIkUs798qc+fOzXR/z5498ygSAACAzF1TUur06dPaunWrTpw4obS0NId9XOgAAOAmJu3yZkU/ko4eParAwEB7satRUpJUunRpeXp6Oo1OOnHihNMopnQhISEu63t5ealUqVKZ1knv81qOez0mT56sX375RcHBwQoLC5O3t7fD/p07d1p+zGsxaNAgh8cXL17UP//8Ix8fHxUpUoRrNQAAkG/kOCn12Wef6eGHH9a5c+cUEBDg9GsmFzoAABQOgYGBDkmpjPj4+KhBgwaKjY1Vp06d7OWxsbG69957XbaJiIjQZ5995lC2du1aNWzY0J7siYiIUGxsrMO6UmvXrlWTJk2u+bjX47777rO8z9zw119/OZUdPHhQTzzxhJ577jk3RAQAAOBajpNSzz77rPr06aMxY8bk66HrAAD819hMmmwWjJS6lj6GDh2qHj16qGHDhoqIiND06dOVkJCgAQMGSJKioqJ0/Phx+9SyAQMG6N1339XQoUPVv39/xcfHa+bMmVq48H9rJg4aNEjNmzfX+PHjde+99+rTTz/VunXrtGnTpmwf10ojR460vM+8ctNNN2ncuHF65JFH9OOPP7o7HAAAAEnXkJQ6fvy4nnnmGRJSAADkNxZP38uJbt266dSpUxo9erQSExNVq1YtrVq1SqGhoZKkxMREJSQk2OuHh4dr1apVGjJkiN577z2VL19ekydPVufOne11mjRpokWLFumll17Syy+/rCpVqmjx4sVq1KhRto9rtdOnT2vJkiX65Zdf9Nxzz6lkyZLauXOngoODVaFChVw5plU8PT3122+/uTsMAAAAuxwnpdq0aaPt27ercuXK133wr7/+Wm+88YZ27NihxMRELV++3GFovDFGo0aN0vTp0/XXX3+pUaNGeu+991SzZs1M+126dKlefvll/fLLL6pSpYpef/11h2H9AADAegMHDtTAgQNd7ps9e7ZTWYsWLbJch6lLly7q0qXLNR/XSnv37tVdd92loKAgHT58WP3791fJkiW1fPlyHTlyJMsFxvPKypUrHR4bY5SYmKh3331XTZs2dVNUAAAAznKclOrQoYOee+45/fDDD6pdu7bTIp/33HNPtvs6d+6c6tSpo0cffdThl9F0EyZM0KRJkzR79mzdfPPNeu2119S6dWsdOHBAAQEBLvuMj49Xt27d9Oqrr6pTp05avny5HnjgAW3atMnhl1UAAAodYy5vVvQDJ0OHDlXv3r01YcIEh+uQdu3aqXv37m6MzNHVa1/ZbDaVKVNGd9xxh9588033BAUAAOBCjpNS/fv3lySNHj3aaZ/NZlNqamq2+2rXrp3atWvncp8xRjExMXrxxRd1//33S5LmzJmj4OBgLViwQI8//rjLdjExMWrdurWioqIkXV7DIi4uTjExMQ7rVAAAUOi4cfref8G2bdv0/vvvO5VXqFDB6Q6A7nT1nZEBAADyK4+cNkhLS8twy0lCKiuHDh1SUlKSIiMj7WW+vr5q0aKFNm/enGG7+Ph4hzbS5SmHmbUBAADIip+fn5KTk53KDxw4oDJlyrghIgAAgIItxyOl8kr6L47BwcEO5cHBwTpy5Eim7Vy1yewXzJSUFKWkpNgfu7rgBAAgv7MZY9Hd95i+58q9996r0aNH6+OPP5Z0eYR4QkKCRowY4XIZAncZOnRotutOmjQpFyMBAADIXLaTUpMnT85WvWeeeeaag3HFZrM5PDbGOJVdb5uxY8dq1KhR1x4kAAAo9CZOnKj27durbNmy+vfff9WiRQslJSUpIiJCr7/+urvDs9u1a5d27typS5cuqWrVqpKkn376SZ6enqpfv769XlbXUwAAALkt20mpt956K8s6NpvNsqRUSEiIpMsjn8qVK2cvP3HihNNIqKvbXT0qKqs2UVFRDr8qJicnq2LFitcaOgAA7sGaUrkqMDBQmzZt0vr167Vz506lpaWpfv36uuuuu9wdmoO7775bAQEBmjNnjkqUKCFJ+uuvv/Too4+qWbNmevbZZ90cIQAAwGXZTkodOnQoN+NwEh4erpCQEMXGxqpevXqSpAsXLiguLk7jx4/PsF1ERIRiY2M1ZMgQe9natWvVpEmTDNv4+vrK19fXuuABAHAHklJ54o477tAdd9zh7jAy9Oabb2rt2rX2hJQklShRQq+99poiIyNJSgEAgHwjxwudW+ns2bPavXu3du/eLely4mv37t1KSEiQzWbT4MGDNWbMGC1fvlz79u1T7969VaRIEYfbLvfs2dN+pz1JGjRokNauXavx48frxx9/1Pjx47Vu3ToNHjw4j88OAAAUJs8884zL5QzefffdfHWdkZycrN9//92p/MSJE/r777/dEBEAAIBrbk1Kbd++XfXq1bOPhBo6dKjq1aunV155RZI0fPhwDR48WAMHDlTDhg11/PhxrV27VgEBAfY+EhISlJiYaH/cpEkTLVq0SLNmzdItt9yi2bNna/HixWrUqFHenhwAAHktfaSUFRucLF26VE2bNnUqb9KkiZYsWeKGiFzr1KmTHn30US1ZskTHjh3TsWPHtGTJEvXt21f333+/u8MDAACwc+vd91q2bCmTyR1+bDaboqOjFR0dnWGdjRs3OpV16dJFXbp0sSBCAAAKEJMmpTF9L7ecOnVKQUFBTuWBgYE6efKkGyJybdq0aRo2bJgeeeQRXbx4UZLk5eWlvn376o033nBzdAAAAP+T7ZFSx44dy804AAAA8rUbb7xRq1evdir/8ssvVblyZTdE5FqRIkU0ZcoUnTp1yn4nvj///FNTpkxR0aJF3R0eAACAXbZHStWqVUvvvPOOevTokZvxAACAa2QzabJZMMrJij4Ko6FDh+qpp57SH3/8YV/o/KuvvtKbb76pmJgY9wbnQmJiohITE9W8eXP5+/vLGCObzebusAAAAOyynZQaM2aMnnzySa1YsULTp09XqVKlcjMuAACAfKVPnz5KSUnR66+/rldffVWSFBYWpqlTp6pnz55uju5/Tp06pQceeEAbNmyQzWbTwYMHVblyZfXr10/FixfXm2++6e4QAQAAJOVg+t7AgQO1Z88e/fXXX6pZs6ZWrlyZm3EBAICcYqHzXPfEE0/o2LFj+v3335WcnKxff/01XyWkJGnIkCHy9vZWQkKCihQpYi/v1q2by+mHAAAA7pKjhc7Dw8O1fv16vfvuu+rcubOqV68uLy/HLnbu3GlpgO50/qs58inqb0lfaX+ftqSfdF6tHrG0P0nyPHPc0v4u+fhZ2p+HX5GsK7lbWqq7I8hbHp6Wd+ldPtzS/i4d229pf143VLe0P8BSxlzerOgHmSpTpoy7Q8jQ2rVrtWbNGt1www0O5TfddJOOHDnipqgAAACc5fjue0eOHNHSpUtVsmRJ3XvvvU5JKQAAgMJqyZIl+vjjj5WQkKALFy447MsvP8ydO3fOYYRUupMnT8rX19cNEQEAALiWo4zSjBkz9Oyzz+quu+7Svn378vWvhAAA/OdYNfWO6XsuTZ48WS+++KJ69eqlTz/9VI8++qh++eUXbdu2TU8++aS7w7Nr3ry55s6da1/3ymazKS0tTW+88YZatWrl5ugAAAD+J9tJqbZt22rr1q169913893aCQAAQLIZY9Hd95i+58qUKVM0ffp0PfTQQ5ozZ46GDx+uypUr65VXXtGff/7p7vDs3njjDbVs2VLbt2/XhQsXNHz4cH3//ff6888/9e2337o7PAAAALtsL3SempqqvXv3kpACAAD/SQkJCWrSpIkkyd/fX3///bckqUePHlq4cKE7Q3NQo0YN7d27V7fddptat26tc+fO6f7779euXbtUpUoVd4cHAABgl+2RUrGxsbkZBwAAuF5M38tVISEhOnXqlEJDQxUaGqotW7aoTp06OnTokEw+GV128eJFRUZG6v3339eoUaPcHQ4AAECmsj1SCgAA4L/sjjvu0GeffSZJ6tu3r4YMGaLWrVurW7du6tSpk5uju8zb21v79u2TzWZzdygAAABZ4tZ5AAAUFoyUylXTp09XWtrl52bAgAEqWbKkNm3apLvvvlsDBgxwc3T/07NnT82cOVPjxo1zdygoJMJGfJFlncPjOuTp8QDkP3n9WYHCgaQUAACFBUmpXOXh4SEPj/8NMn/ggQf0wAMPuDEi1y5cuKAPPvhAsbGxatiwoYoWLeqwf9KkSW6KDAAAwBFJKQAAgAzs3bs323VvueWWXIwka7/++qvCwsK0b98+1a9fX5L0008/OdRhWh8AAMhPSEoBAFBI2EyabBaMcrKij8Kibt26stlsWS5kbrPZlJqamkdRuXbTTTcpMTFRGzZskCR169ZNkydPVnBwsFvjAgAAyAhJKQAACou0tMubFf1AknTo0CF3h5BtVyfOvvzyS507d85N0QAAAGSNpBQAAEAGQkND3R3CNctqdBcAAIC7kZQCAKCwMObyZkU/cOnAgQN65513tH//ftlsNlWrVk1PP/20qlat6u7QZLPZnNaMYg0pAACQn5GUAgCgsODue7lqyZIleuihh9SwYUNFRERIkrZs2aJatWppwYIF6tq1q1vjM8aod+/e8vX1lSSdP39eAwYMcLr73rJly9wRHgAAgBOSUgAAANkwfPhwRUVFafTo0Q7lI0eO1PPPP+/2pFSvXr0cHj/yyCNuigQAACB7SEoBAFBIcPe93JWUlKSePXs6lT/yyCN644033BCRo1mzZrk7BAAAgBzxcHcAAAAABUHLli31zTffOJVv2rRJzZo1c0NEAAAABRsjpQAAKCxYUypX3XPPPXr++ee1Y8cONW7cWNLlNaU++eQTjRo1SitXrnSoCwAAgMyRlAIAoLAwxqKkFHffc2XgwIGSpClTpmjKlCku90mX73iXmpqap7EBAAAURCSl8oqHp6XdXVzzgaX9SdL58ymW9peWau0v7f43Vre0v7Rzf1vanySl/WNtn1bPr009f87iHq1n8/GztL+0k4mW9nch4XtL+/Np0sXS/gDknrQ0RpAB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vlr3JFq1ahX27NmDuLg45OXlYcuWLfDw8FAp89FHH2HYsGE4efIkXn/9dYwaNQqnT5/WGK+2thbt2rVDXFwccnNzsWDBAnzwwQeIi4tTllmzZg2mTp2KiIgI5OTkYM+ePejUqRMAQAiBQYMGobi4GAkJCcjMzIS/vz9eeOEFXLt2TRkjPz8fe/fuRWJiIr7++mvExMRg0KBB+OWXX5CamoolS5bgww8/REZGRqPj7t69G9999x2+++47pKamYvHixQCAlStXIjAwEJMmTUJRURGKiorQvn17je/DoUOH0L17d7Xjt27dwqpVq7Bt2zYkJiYiJSUFQ4cORUJCAhISErB582asW7cO33zzjUq9nj17qqwCICLNrJq6AUSkXzExMbC3t0dBQQF++eUXtf+0NMRTTz2l/LdMJoNCocDly5cBAKdPn4avry/s7e2VZQIDA1XqZ2ZmIjk5GS1atFCLnZ+fjyeeeAI2NjbYsmULnnrqKXTo0AGfffZZo9tJREREZA58fHxw6tQpjecuXLgALy8vPPvss5DJZOjQoYNamb/+9a+YOHEiAODvf/87kpKS8Pnnn2P16tVqZa2trbFo0SLlc09PT6SlpSEuLk45q/0f//gH3n77bcycOVNZrkePHgCA5ORk5OTk4PLly8olhp9++il2796Nb775BhEREQDuJr9iYmLg4OCAzp07o1+/fsjLy0NCQgIsLCzg7e2NJUuWICUlBQEBAY2KGxsbq9wHKiwsDAcOHMDHH38MuVwOGxsb2NvbQ6FQ6HzPCwsL0a1bN7Xjd+7cwZo1a9CxY0cAwGuvvYbNmzfj0qVLaNGihbIvycnJGDFihLJe27ZtceHCBdTW1sLCgnNBiLRhUorIjKWnp2PFihXYu3cvli5digkTJmD//v2QyWSNimNtba3yXCaTKTduFELUW7+2thYvv/wylixZonbu3uV5aWlpAIBr167h2rVraN68eaPaSURERGQOhBBa/782btw4vPTSS/D29kZwcDBCQ0MRFBSkUub+C4SBgYE677b31Vdf4Z///CfOnz+PiooKVFVVKbd8uHz5Mn777Te88MILGutmZmbi5s2baN26tcrxiooK5OfnK597eHiobCDu4uICS0tLlYSNi4uL8sLng8Z1dXVVxmiMiooK2NnZqR23t7dXJqTq2ujh4aFysfXedtdp1qwZamtrUVlZiWbNmjW6PUSPCialiMxURUUFwsPD8eabb+LFF1/EE088gS5dumDt2rWYPHmyZK/TuXNnbN68GRUVFcpfuHXTruv4+/tj586d8PDwgJWV5q+d/Px8zJ49G+vXr0dcXBzGjh2LAwcO8MoSERERPXJOnz4NT09Pjef8/f1RUFCAvXv3Yv/+/Rg+fDhefPFFteVj99OW5IqLi8Ps2bOxbNkyBAYGwsHBAZ988gmOHDkCAPUmVGpra+Hq6qrc2uFejz32mPLfmi5y6rrw+TBxH+Sud05OTrh+/bra8ca2u861a9dgb2/PhBRRPfjXHpGZqrsNbd3sJHd3dyxbtgzvvPOO1g0eH8To0aNhYWGBCRMmIDc3FwkJCfj0009VykydOhXXrl3DqFGjcPToUfz888/44Ycf8MYbb6CmpgY1NTUICwtDUFAQxo8fj40bN+LHH3/EsmXLJGsnERERkSk4ePAgcnJyMGzYMK1lHB0dMWLECKxfvx7bt2/Hzp07VfZZuv8CYUZGBnx8fDTGOnz4MHr37o0pU6bAz88PnTp1UpmJ5ODgAA8PDxw4cEBjfX9/fxQXF8PKygqdOnVSeTg5OTWm63qJa2Njo7IJuTZ+fn7Izc194Pbe78cff4S/v79k8YjMFZNSRGYoNTUVX375JWJjY1WWwE2aNAm9e/fGhAkTGrTsriFatGiBf//738jNzYWfnx/mz5+vtkzPzc0N//3vf1FTU4MBAwagS5cumDlzJuRyOSwsLPDxxx+jsLAQ69atA3D3tsf//Oc/8eGHH+qcak5ERERkyiorK1FcXIxff/0VJ06cQFRUFIYMGYLQ0FCMHTtWY50VK1Zg27Zt+Omnn3DmzBns2LEDCoVCZfbQjh07EBMTgzNnzmDhwoU4evSoyt3y7tWpUyccP34c+/btw5kzZ/DRRx/h2LFjKmUiIyOxbNkyrFq1CmfPnsWJEyfw+eefAwBefPFFBAYG4pVXXsG+fftQWFiItLQ0fPjhhzh+/PgDvzdSxfXw8MCRI0dQWFiIK1euaJ1FNWDAAKSnpzcogdUQhw8fVltWSUTquHyPyAz17dsX1dXVGs/t27dPZ937p0hrmlV1f6IoICBA7dj9SS8vLy+td5FZsGABFixYoHJsyJAhqKys1NlWIiIiIlOWmJgIV1dXWFlZoWXLlvD19cWqVasQHh6udQuDFi1aYMmSJTh79iwsLS3Ro0cP5WbhdRYtWoRt27ZhypQpUCgU2Lp1Kzp37qwx3uTJk5GdnY0RI0ZAJpNh1KhRmDJlCvbu3assEx4ejtu3b2PFihWYO3cunJyc8NprrwG4u3QtISEB8+fPxxtvvIHff/8dCoUCzz33HFxcXB74vZEq7ty5cxEeHo7OnTujoqICBQUFGm/8M3DgQFhbW2P//v0YMGDAA7cbAH799VekpaVhy5YtDxWH6FEgE1JNlyAiIiIiIqImJZPJEB8fj1deeaWpm2JyVq9ejW+//bbei7j1eeedd1BSUqJcBUBE2nGmFBERERERET3yIiIicP36dZSVlanc0a+xnJ2dMXfuXAlbRmS+OFOKiIiIiIjITHCmFBGZEs6UIiIiIiIiMhOcc0BEpoR33yMiIiIiIiIiIoNjUoqIiIiIiIiIiAyOSSkiIiIiIiIiIjI4JqWIiIiIiIiIiMjgmJQiIiIiIiIiIiKDY1KKiIiIiIiIiIgMjkkpIiIiIiIiIiIyOCaliIiIiIiIiIjI4JiUIiIiIiIiIiIig/t/IPVtns/uOBMAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "subset_vals = subset.values.flatten()\n", "subset_vals = subset_vals[~np.isnan(subset_vals)]\n", "\n", "fig, axes = plt.subplots(1, 2, figsize=(12, 5))\n", "\n", "# Panel 1: Map of the subset\n", "im = axes[0].imshow(subset.values, cmap=\"RdBu\", vmin=-0.1, vmax=0.1)\n", "axes[0].set_title(\"Subset Displacement Map\")\n", "axes[0].set_xlabel(\"X Index\")\n", "axes[0].set_ylabel(\"Y Index\")\n", "fig.colorbar(im, ax=axes[0], orientation='vertical', label=\"Displacement (m)\")\n", "\n", "# Panel 2: Histogram\n", "axes[1].hist(subset_vals, bins=50)\n", "axes[1].set_title(\"Histogram of Displacement (Index-Based Subset)\")\n", "axes[1].set_xlabel(\"Displacement (m)\")\n", "axes[1].set_ylabel(\"Frequency\")\n", "\n", "plt.tight_layout()\n", "plt.show()\n" ] }, { "cell_type": "markdown", "id": "9b076378-7861-4317-965f-a0c2bff81cb6", "metadata": {}, "source": [ "### File Space and Chunk Layout Analysis\n", "\n", "Building on the above section, it is important to understand how scientific data files are internally organized. Variables such as displacement or temporal coherence are divided into two-dimensional chunks, which act as independent blocks that can be selectively accessed. Because only the chunks intersecting a user’s query need to be fetched, this structure enables partial reads and reduces unnecessary data transfer. When paired with tuned I/O parameters like buffer sizes and cache settings, chunked layouts make cloud-optimized access possible, ensuring that workflows remain efficient even for very large files. This analysis of file space and chunk organization provides the structural context behind the performance gains observed during remote streaming.\n" ] }, { "cell_type": "markdown", "id": "c2d6c984-3c4b-45af-aa3d-4d7ef764ece3", "metadata": {}, "source": [ "The file uses a paged free-space management strategy (`H5F_FSPACE_STRATEGY_PAGE`) with a page size of about 4 MB. The raw data accounts for roughly 389 MB, while metadata contributes less than 1 MB, bringing the total file size to about 394 MB. " ] }, { "cell_type": "code", "execution_count": 12, "id": "3954211d-deeb-4cb5-a69d-8ff2cb28c5dd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Filename: OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20160725T005759Z_v1.0_20250412T124848Z.nc\n", "File space management strategy: H5F_FSPACE_STRATEGY_PAGE\n", "File space page size: 4194304 bytes\n", "Summary of file space information:\n", " File metadata: 917216 bytes\n", " Raw data: 389683119 bytes\n", " Amount/Percent of tracked free space: 0 bytes/0.0%\n", " Unaccounted space: 3664241 bytes\n", "Total space: 394264576 bytes\n" ] } ], "source": [ "!h5stat -S OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20160725T005759Z_v1.0_20250412T124848Z.nc" ] }, { "cell_type": "markdown", "id": "4c3d39ff-ff9f-45e6-a86f-89299ff0b33c", "metadata": {}, "source": [ "**Observation**:\n", "- **Strategy:** `H5F_FSPACE_STRATEGY_PAGE` (paged free-space management)\n", "- **Page size:** 4,194,304 bytes (**4 MB**)\n", "- **Raw data size:** ~389.7 MB\n", "- **Metadata size:** ~0.9 MB\n", "- **Total file size:** ~394.3 MB\n" ] }, { "cell_type": "markdown", "id": "d4e1f8f1-d91d-458b-8554-172b0ff582b5", "metadata": {}, "source": [ "The displacement datasets are stored in a chunked layout of `(256, 256)` with shuffle and deflate compression at level 4. This structure allows efficient partial access and reduces storage size through ~3.5–3.8x compression. \n" ] }, { "cell_type": "code", "execution_count": 15, "id": "37c548ac-2048-44e9-abff-feb72bb3467c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " DATASET \"displacement\" {\n", " DATATYPE H5T_IEEE_F32LE\n", " DATASPACE SIMPLE { ( 8036, 9614 ) / ( 8036, 9614 ) }\n", " STORAGE_LAYOUT {\n", " CHUNKED ( 256, 256 )\n", " SIZE 81878337 (3.774:1 COMPRESSION)\n", " }\n", " FILTERS {\n", " PREPROCESSING SHUFFLE\n", " COMPRESSION DEFLATE { LEVEL 4 }\n", " }\n", "--\n", " DATASET \"short_wavelength_displacement\" {\n", " DATATYPE H5T_IEEE_F32LE\n", " DATASPACE SIMPLE { ( 8036, 9614 ) / ( 8036, 9614 ) }\n", " STORAGE_LAYOUT {\n", " CHUNKED ( 256, 256 )\n", " SIZE 88318187 (3.499:1 COMPRESSION)\n", " }\n", " FILTERS {\n", " PREPROCESSING SHUFFLE\n", " COMPRESSION DEFLATE { LEVEL 4 }\n", " }\n" ] } ], "source": [ "!h5dump -pH OPERA_L3_DISP-S1_IW_F40292_VV_20160701T005758Z_20160725T005759Z_v1.0_20250412T124848Z.nc | grep displacement -A 10\n", " " ] }, { "cell_type": "markdown", "id": "08597ff3-05e6-493b-98de-b6c5872a7ced", "metadata": {}, "source": [ "**Observation**:\n", "- **Raw size of `displacement`:** ≈ 8036 × 9614 × 4 bytes ≈ 309 MB\n", "- **Chunk geometry:** 256 × 256 → each uncompressed chunk = 256 × 256 × 4 bytes = 256 KiB\n", "- **Approx. number of chunks:** ⌈8036/256⌉ × ⌈9614/256⌉ = 32 × 38 = 1216 chunks\n" ] }, { "cell_type": "markdown", "id": "fa9f3e12-b5af-4995-8e5e-2686ec9cd47c", "metadata": {}, "source": [ "### Why This Matters\n", "\n", "- The displacement variable has a raw size of ~309 MB, stored in chunks of `256 × 256` (≈256 KB each). \n", "- With compression (~3.7:1), chunks shrink to ~70 KB on average, making them efficient to stream. \n", "- There are ~1200 chunks in total, so grouping them into larger blocks helps reduce overhead. \n", "\n", "### Implications\n", "\n", "- Crossing chunk boundaries adds extra reads/decompression. \n", "- Dask/xarray should chunk in multiples of `(256, 256)` (e.g., 512×512 or 1024×1024). \n", "- Each task should pull MBs of data (not KBs) to balance performance and memory. \n", "- The 4 MB free-space paging aligns well with these access patterns. \n", "\n", "### The Optimization Parameters We Set\n", "\n", "- Dask/xarray chunking: `1024 × 1024` → ~4 MB uncompressed per task, ~1–2 MB compressed. \n", "- Time-stacked collections keep per-time tasks small and efficient. \n", "- Windowed reads should align to 256-pixel boundaries to avoid partial-chunk penalties. \n", "- Parallelism works best with task sizes in the 1–8 MB range and worker memory in the few-GB range. \n", "\n", "See the [Cloud-Optimized NetCDF4/HDF5 Guide](https://guide.cloudnativegeo.org/cloud-optimized-netcdf4-hdf5/) for more details. \n" ] }, { "cell_type": "markdown", "id": "5dff765b-ac24-4bca-b118-c7b51e01c303", "metadata": {}, "source": [ "## Using OPERA DISP in a DPS job" ] }, { "cell_type": "markdown", "id": "2fadcbd1-edf5-447f-bc42-2d90a24c6f59", "metadata": {}, "source": [ "Refer [DPS JOB](https://github.com/MAAP-Project/OPERA_DPS_JOB) for running a job in the MAAP ADE to process the OPERA Surface Displacement dataset and extract the results" ] } ], "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.12.7" } }, "nbformat": 4, "nbformat_minor": 5 }