Skip to content

Commit 50162a7

Browse files
hrodmnchuckwondo
andauthored
feat: add group inspection to compatibility endpoint (#186)
Add support for hierarchically organized datasets in the compatibility endpoint Co-authored-by: Chuck Daniels <chuck@developmentseed.org>
1 parent 93abf5a commit 50162a7

5 files changed

Lines changed: 708 additions & 195 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ node_modules
106106

107107
notebooks/
108108
.envrc
109+
issue-drafts/
109110

110111
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode
111112
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ See also the [API documentation](https://staging.openveda.cloud/api/titiler-cmr/
2222

2323
- Render tiles from assets discovered via queries to [NASA's CMR](https://cmr.earthdata.nasa.gov/search)
2424
- Two backends: **xarray** (`/xarray`) for NetCDF/HDF5 datasets and **rasterio** (`/rasterio`) for GeoTIFF/COG assets
25+
- `/compatibility` helps inspect sample granules, including optional `group` selection and lightweight group discovery for hierarchical HDF5 and NetCDF assets
2526
- Timeseries endpoints for generating GIFs, statistics, and tilejsons across a temporal range
2627
- Queries CMR directly via the [granule search API](https://cmr.earthdata.nasa.gov/search/site/docs/search/api.html)
2728
- Built on top of [titiler](https://github.com/developmentseed/titiler)

docs/api/compatibility_api_example.ipynb

Lines changed: 112 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,74 @@
22
"cells": [
33
{
44
"cell_type": "markdown",
5-
"id": "cdeef4c6-75b0-44d9-90d4-c850b5d8908a",
5+
"id": "intro",
66
"metadata": {},
77
"source": [
88
"# How to use the Compatibility API\n",
99
"\n",
10-
"The `/compatibility` endpoint displays information about the collection and returns some details about a sample granule. The output is helpful for understanding the structure of the collection and the granules so that you can craft the right set of parameters for visualization or statistics requests.\n",
10+
"The `/compatibility` endpoint helps you understand how to call TiTiler-CMR for a collection. For hierarchical HDF5 and NetCDF datasets it can return a lightweight list of candidate `group` paths from the root request, then let you inspect variables by making a follow-up request with `group=...`.\n",
1111
"\n",
12-
"This notebook demonstrates how to use the compatibility endpoint for th [GHRSST Level 4 MUR Global Foundation Sea Surface Temperature Analysis (v4.1)](https://cmr.earthdata.nasa.gov/search/concepts/C1996881146-POCLOUD) dataset.\n",
12+
"This notebook demonstrates that workflow for the [NISAR Beta Geocoded Polarimetric Covariance (GCOV)](https://www.earthdata.nasa.gov/data/catalog/asf-nisar-l2-gcov-beta-v1-1) collection, which is a known example of a hierarchical HDF5 dataset that needs a `group` parameter for xarray requests.\n",
1313
"\n",
1414
"## Setup"
1515
]
1616
},
1717
{
1818
"cell_type": "code",
1919
"execution_count": null,
20-
"id": "7d015182-5347-437a-8b66-d7d62212f0e3",
20+
"id": "setup",
2121
"metadata": {},
2222
"outputs": [],
2323
"source": [
24+
"import json\n",
2425
"import os\n",
2526
"\n",
26-
"import json\n",
2727
"import earthaccess\n",
2828
"import httpx2 as httpx\n",
2929
"\n",
3030
"titiler_endpoint = os.getenv(\n",
31-
" \"TITILER_CMR_ENDPOINT\", \"https://openveda.cloud/api/titiler-cmr\"\n",
31+
" \"TITILER_CMR_ENDPOINT\", \"https://staging.openveda.cloud/api/titiler-cmr\"\n",
3232
")"
3333
]
3434
},
3535
{
3636
"cell_type": "markdown",
37-
"id": "d375b5b7-9322-4f1e-8859-000ef8ac4898",
37+
"id": "identify-dataset",
3838
"metadata": {},
3939
"source": [
4040
"## Identify the dataset\n",
4141
"\n",
42-
"You can find the MUR SST dataset using the `earthaccess.search_datasets` function."
42+
"You can find the collection with `earthaccess.search_datasets`."
4343
]
4444
},
4545
{
4646
"cell_type": "code",
4747
"execution_count": null,
48-
"id": "f3fcc9cd-6105-42fc-98bf-2de40910a79c",
48+
"id": "search-dataset",
4949
"metadata": {},
5050
"outputs": [],
5151
"source": [
52-
"datasets = earthaccess.search_datasets(concept_id=\"C1996881146-POCLOUD\")\n",
52+
"datasets = earthaccess.search_datasets(concept_id=\"C3622214170-ASF\")\n",
5353
"ds = datasets[0]\n",
5454
"\n",
5555
"collection_concept_id = ds[\"meta\"][\"concept-id\"]\n",
56-
"print(\"CollectionConcept-Id: \", collection_concept_id)\n",
57-
"\n",
58-
"print(\"Abstract: \", ds[\"umm\"][\"Abstract\"])"
56+
"print(\"CollectionConcept-Id:\", collection_concept_id)\n",
57+
"print(\"Short name:\", ds[\"umm\"][\"ShortName\"])\n",
58+
"print(\"Version:\", ds[\"umm\"][\"Version\"])"
5959
]
6060
},
6161
{
6262
"cell_type": "markdown",
63-
"id": "2a4cffa6-0059-4033-a708-db60d743f0e3",
63+
"id": "explore-collection",
6464
"metadata": {},
6565
"source": [
66-
"## Explore the collection using the `/compatibility` endpoint"
66+
"## Explore the collection using `/compatibility`"
6767
]
6868
},
6969
{
7070
"cell_type": "code",
7171
"execution_count": null,
72-
"id": "1bde609a-26df-4f35-b7e1-9e1922e87808",
72+
"id": "compatibility-request",
7373
"metadata": {},
7474
"outputs": [],
7575
"source": [
@@ -84,12 +84,105 @@
8484
},
8585
{
8686
"cell_type": "markdown",
87-
"id": "04014a32-9c11-4b75-b40a-e5ad4efd686b",
87+
"id": "interpret-response",
88+
"metadata": {},
89+
"source": [
90+
"## Interpret the response\n",
91+
"\n",
92+
"For hierarchical datasets, a root `/compatibility` request stays lightweight. The most important field is:\n",
93+
"\n",
94+
"- `compatible_groups`: candidate group paths to try in a follow-up request\n",
95+
"\n",
96+
"At this stage the response may not include variable metadata or xarray links yet, because TiTiler-CMR has not inspected a nested group."
97+
]
98+
},
99+
{
100+
"cell_type": "code",
101+
"execution_count": null,
102+
"id": "summarize-response",
103+
"metadata": {},
104+
"outputs": [],
105+
"source": [
106+
"compatible_groups = compatibility_response.get(\"compatible_groups\", [])\n",
107+
"variables = list((compatibility_response.get(\"variables\") or {}).keys())\n",
108+
"tilejson_link = next(\n",
109+
" (\n",
110+
" link[\"href\"]\n",
111+
" for link in compatibility_response.get(\"links\", [])\n",
112+
" if link[\"rel\"] == \"tilejson\"\n",
113+
" ),\n",
114+
" None,\n",
115+
")\n",
116+
"\n",
117+
"print(\"compatible_groups:\")\n",
118+
"for group in compatible_groups:\n",
119+
" print(\" -\", group)"
120+
]
121+
},
122+
{
123+
"cell_type": "markdown",
124+
"id": "inspect-explicit-group",
125+
"metadata": {},
126+
"source": [
127+
"## Inspect a specific group\n",
128+
"\n",
129+
"Once you choose a group from `compatible_groups`, make a follow-up `/compatibility` request with `group=...` to inspect variables and get xarray links."
130+
]
131+
},
132+
{
133+
"cell_type": "code",
134+
"execution_count": null,
135+
"id": "compatibility-request-with-group",
136+
"metadata": {},
137+
"outputs": [],
138+
"source": [
139+
"selected_group = compatible_groups[1]\n",
140+
"\n",
141+
"grouped_compatibility_response = httpx.get(\n",
142+
" f\"{titiler_endpoint}/compatibility\",\n",
143+
" params={\n",
144+
" \"collection_concept_id\": collection_concept_id,\n",
145+
" \"group\": selected_group,\n",
146+
" },\n",
147+
" timeout=30,\n",
148+
").json()\n",
149+
"\n",
150+
"print(json.dumps(grouped_compatibility_response, indent=2))"
151+
]
152+
},
153+
{
154+
"cell_type": "code",
155+
"execution_count": null,
156+
"id": "summarize-grouped-response",
157+
"metadata": {},
158+
"outputs": [],
159+
"source": [
160+
"group_variables = list((grouped_compatibility_response.get(\"variables\") or {}).keys())\n",
161+
"group_tilejson_link = next(\n",
162+
" link[\"href\"]\n",
163+
" for link in grouped_compatibility_response.get(\"links\", [])\n",
164+
" if link[\"rel\"] == \"tilejson\"\n",
165+
")\n",
166+
"\n",
167+
"print(\"selected group:\", selected_group)\n",
168+
"print(\"variables in selected group (first 10):\", group_variables[:10])\n",
169+
"print(\"tilejson link for selected group:\", group_tilejson_link)"
170+
]
171+
},
172+
{
173+
"cell_type": "markdown",
174+
"id": "what-to-do-next",
88175
"metadata": {},
89176
"source": [
90-
"The details from the sample granule show that it is a NetCDF file with four variables (`analysed_sst`, `analysis_error`, `mask`, and `sea_ice_fraction`) and each contains an array with a single time coordinate. The `datetime` key shows the reported temporal range from CMR which indicates that the dataset has granules from `2008-07-23` to present. For each variable several summary statistics are available to help you craft min/max values for the `rescale` parameter.\n",
177+
"For a grouped dataset like NISAR GCOV, the workflow is:\n",
178+
"\n",
179+
"1. Call `/compatibility` without `group` to get candidate `compatible_groups`.\n",
180+
"2. Choose one of those group paths.\n",
181+
"3. Call `/compatibility` again with `group=...` to inspect variables and dimensions for that group.\n",
182+
"4. Pick a variable name from `variables`.\n",
183+
"5. Reuse the returned xarray links from the grouped response.\n",
91184
"\n",
92-
"This information is handy for generating tiles via the tiles API, which you can learn more about in [the Tiles API Documentation](../tiling_api_xarray_backend_example)."
185+
"That keeps the root compatibility response lightweight while still giving you a practical path to the next xarray request."
93186
]
94187
}
95188
],

0 commit comments

Comments
 (0)