Skip to content

Commit 3e28dcd

Browse files
Add terrascope module for Terrascope STAC API integration (#1294)
* Add terrascope module for Terrascope STAC API integration - Add leafmap/terrascope.py with OAuth2 auth, token caching, and STAC search - Add docs/notebooks/115_terrascope.ipynb example notebook - Add API documentation - Update mkdocs.yml * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update notebook * Address Copilot and CodeQL review comments - Add threading.Lock for token cache thread safety - Add REQUEST_TIMEOUT (30s) to all requests.post calls - Remove unused ItemCollection import - Add logging for background refresh errors - Unset GDAL env vars in logout() - Add explanatory comments for exception handling - Document OAuth2 password grant security in docstrings * Update notebook * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 52f9fc7 commit 3e28dcd

4 files changed

Lines changed: 780 additions & 0 deletions

File tree

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "0",
6+
"metadata": {},
7+
"source": [
8+
"[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=notebooks/115_terrascope.ipynb)\n",
9+
"[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/notebooks/115_terrascope.ipynb)\n",
10+
"[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n",
11+
"\n",
12+
"# Terrascope STAC API with leafmap\n",
13+
"\n",
14+
"This notebook demonstrates how to access Terrascope data using the `leafmap.terrascope` module.\n",
15+
"\n",
16+
"**Setup:** Set environment variables before running:\n",
17+
"```bash\n",
18+
"export TERRASCOPE_USERNAME='your_username'\n",
19+
"export TERRASCOPE_PASSWORD='your_password'\n",
20+
"```"
21+
]
22+
},
23+
{
24+
"cell_type": "code",
25+
"execution_count": null,
26+
"id": "1",
27+
"metadata": {},
28+
"outputs": [],
29+
"source": [
30+
"import leafmap\n",
31+
"import leafmap.terrascope as terrascope"
32+
]
33+
},
34+
{
35+
"cell_type": "markdown",
36+
"id": "2",
37+
"metadata": {},
38+
"source": [
39+
"## Authentication"
40+
]
41+
},
42+
{
43+
"cell_type": "code",
44+
"execution_count": null,
45+
"id": "3",
46+
"metadata": {},
47+
"outputs": [],
48+
"source": [
49+
"terrascope.login()"
50+
]
51+
},
52+
{
53+
"cell_type": "markdown",
54+
"id": "4",
55+
"metadata": {},
56+
"source": [
57+
"## Search for NDVI Data"
58+
]
59+
},
60+
{
61+
"cell_type": "code",
62+
"execution_count": null,
63+
"id": "5",
64+
"metadata": {},
65+
"outputs": [],
66+
"source": [
67+
"bbox = [5.032597, 51.220809, 5.055170, 51.234246]\n",
68+
"\n",
69+
"items = terrascope.search_ndvi(\n",
70+
" bbox=bbox,\n",
71+
" start=\"2025-05-01\",\n",
72+
" end=\"2025-06-01\",\n",
73+
" max_cloud_cover=10,\n",
74+
")\n",
75+
"\n",
76+
"print(f\"Found {len(items)} scenes:\")\n",
77+
"for date in terrascope.get_item_dates(items):\n",
78+
" print(f\" {date}\")"
79+
]
80+
},
81+
{
82+
"cell_type": "markdown",
83+
"id": "6",
84+
"metadata": {},
85+
"source": [
86+
"## Single Layer Visualization"
87+
]
88+
},
89+
{
90+
"cell_type": "code",
91+
"execution_count": null,
92+
"id": "7",
93+
"metadata": {},
94+
"outputs": [],
95+
"source": [
96+
"center = [(bbox[1] + bbox[3]) / 2, (bbox[0] + bbox[2]) / 2]\n",
97+
"\n",
98+
"m = leafmap.Map(center=center, zoom=14)\n",
99+
"m.add_raster(\n",
100+
" items[0].assets[\"NDVI\"].href,\n",
101+
" layer_name=f\"NDVI {items[0].datetime.date()}\",\n",
102+
" colormap=\"RdYlGn\",\n",
103+
" vmin=0,\n",
104+
" vmax=250,\n",
105+
")\n",
106+
"m"
107+
]
108+
},
109+
{
110+
"cell_type": "markdown",
111+
"id": "8",
112+
"metadata": {},
113+
"source": [
114+
"## Time Slider Animation"
115+
]
116+
},
117+
{
118+
"cell_type": "code",
119+
"execution_count": null,
120+
"id": "9",
121+
"metadata": {},
122+
"outputs": [],
123+
"source": [
124+
"layers = terrascope.create_time_layers(items[:3]) # Limit to 3 for demo\n",
125+
"\n",
126+
"m = leafmap.Map(center=center, zoom=14)\n",
127+
"m.add_time_slider(layers, time_interval=1)\n",
128+
"m"
129+
]
130+
},
131+
{
132+
"cell_type": "markdown",
133+
"id": "10",
134+
"metadata": {},
135+
"source": [
136+
"## Data Analysis with rioxarray"
137+
]
138+
},
139+
{
140+
"cell_type": "code",
141+
"execution_count": null,
142+
"id": "11",
143+
"metadata": {},
144+
"outputs": [],
145+
"source": [
146+
"import rioxarray\n",
147+
"import numpy as np\n",
148+
"\n",
149+
"# Clean up stale tile servers before analysis\n",
150+
"terrascope.cleanup_tile_servers()\n",
151+
"\n",
152+
"first_item = items[0]\n",
153+
"print(f\"Analyzing: {first_item.datetime.date()}\")\n",
154+
"\n",
155+
"with rioxarray.open_rasterio(first_item.assets[\"NDVI\"].href, mask_and_scale=True) as ds:\n",
156+
" clipped = ds.rio.clip_box(*bbox, crs=\"EPSG:4326\")\n",
157+
" data = clipped.sel(band=1).values\n",
158+
"\n",
159+
" print(f\"\\nNDVI Statistics:\")\n",
160+
" print(f\" Min: {np.nanmin(data):.2f}\")\n",
161+
" print(f\" Max: {np.nanmax(data):.2f}\")\n",
162+
" print(f\" Mean: {np.nanmean(data):.2f}\")"
163+
]
164+
},
165+
{
166+
"cell_type": "markdown",
167+
"id": "12",
168+
"metadata": {},
169+
"source": [
170+
"## Explore Available Collections"
171+
]
172+
},
173+
{
174+
"cell_type": "code",
175+
"execution_count": null,
176+
"id": "13",
177+
"metadata": {},
178+
"outputs": [],
179+
"source": [
180+
"collections = terrascope.list_collections()\n",
181+
"print(f\"Available collections ({len(collections)}):\")\n",
182+
"for c in sorted(collections):\n",
183+
" print(f\" {c}\")"
184+
]
185+
},
186+
{
187+
"cell_type": "code",
188+
"execution_count": null,
189+
"id": "14",
190+
"metadata": {},
191+
"outputs": [],
192+
"source": [
193+
"# Optional: logout when done\n",
194+
"# terrascope.logout()"
195+
]
196+
}
197+
],
198+
"metadata": {
199+
"kernelspec": {
200+
"display_name": "geo",
201+
"language": "python",
202+
"name": "python3"
203+
},
204+
"language_info": {
205+
"name": "python",
206+
"version": "3.12.12"
207+
}
208+
},
209+
"nbformat": 4,
210+
"nbformat_minor": 5
211+
}

docs/terrascope.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# terrascope module
2+
3+
::: leafmap.terrascope

0 commit comments

Comments
 (0)