Skip to content

Commit a76fd4d

Browse files
giswqsCopilotpre-commit-ci[bot]
authored
Add support for visualizing zarr data (#1284)
* Add support for visualizing zarr data * Update leafmap/stac.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update leafmap/stac.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update leafmap/maplibregl.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update leafmap/stac.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update leafmap/leafmap.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 0013321 commit a76fd4d

5 files changed

Lines changed: 1158 additions & 0 deletions

File tree

docs/notebooks/111_zarr.ipynb

Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=notebooks/111_zarr.ipynb)\n",
8+
"[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/notebooks/111_zarr.ipynb)\n",
9+
"\n",
10+
"**Visualizing Zarr Data**\n",
11+
"\n",
12+
"This notebook demonstrates how to visualize Zarr datasets on interactive maps using leafmap. [Zarr](https://zarr.dev/) is a cloud-optimized format for storing large N-dimensional arrays, making it ideal for geospatial and scientific data.\n",
13+
"\n",
14+
"The `add_zarr` method uses [titiler-xarray](https://developmentseed.org/titiler/packages/xarray) for dynamic tile serving from Zarr datasets.\n",
15+
"\n",
16+
"**References:**\n",
17+
"- [EOPF Sentinel Zarr Explorer](https://explorer.eopf.copernicus.eu/)\n",
18+
"- [titiler-eopf](https://github.com/EOPF-Explorer/titiler-eopf)\n",
19+
"- [EOPF 101](https://eopf-toolkit.github.io/eopf-101/)\n",
20+
"- [Zarr Visualization Report](https://nasa-impact.github.io/zarr-visualization-report/)"
21+
]
22+
},
23+
{
24+
"cell_type": "markdown",
25+
"metadata": {},
26+
"source": [
27+
"## Prerequisites\n",
28+
"\n",
29+
"To visualize Zarr data, you need a TiTiler endpoint with titiler-xarray support. The default TiTiler endpoint does NOT support Zarr/xarray datasets.\n",
30+
"\n",
31+
"You have two options:\n",
32+
"\n",
33+
"1. **Start a local titiler-xarray server** (recommended for testing)\n",
34+
"2. **Use a remote titiler-xarray endpoint** (if available)\n",
35+
"\n",
36+
"### Install required packages"
37+
]
38+
},
39+
{
40+
"cell_type": "code",
41+
"execution_count": null,
42+
"metadata": {},
43+
"outputs": [],
44+
"source": [
45+
"# %pip install -U leafmap \"titiler.xarray[full]\" uvicorn xarray zarr fsspec aiohttp"
46+
]
47+
},
48+
{
49+
"cell_type": "code",
50+
"execution_count": null,
51+
"metadata": {},
52+
"outputs": [],
53+
"source": [
54+
"import leafmap"
55+
]
56+
},
57+
{
58+
"cell_type": "markdown",
59+
"metadata": {},
60+
"source": [
61+
"## Option 1: Start a Local TiTiler-XArray Server\n",
62+
"\n",
63+
"The easiest way to get started is to run a local titiler-xarray server. This requires the `titiler.xarray` package to be installed."
64+
]
65+
},
66+
{
67+
"cell_type": "code",
68+
"execution_count": null,
69+
"metadata": {},
70+
"outputs": [],
71+
"source": [
72+
"# Start the local titiler-xarray server\n",
73+
"# This will return the endpoint URL\n",
74+
"endpoint = leafmap.run_titiler_xarray()\n",
75+
"print(f\"TiTiler-XArray endpoint: {endpoint}\")"
76+
]
77+
},
78+
{
79+
"cell_type": "markdown",
80+
"metadata": {},
81+
"source": [
82+
"## Working with Zarr Metadata\n",
83+
"\n",
84+
"Before visualizing, let's explore the Zarr dataset using the helper functions."
85+
]
86+
},
87+
{
88+
"cell_type": "markdown",
89+
"metadata": {},
90+
"source": [
91+
"### Get Available Variables\n",
92+
"\n",
93+
"Use the `zarr_variables` function to list all variables in a Zarr dataset."
94+
]
95+
},
96+
{
97+
"cell_type": "code",
98+
"execution_count": null,
99+
"metadata": {},
100+
"outputs": [],
101+
"source": [
102+
"# GPCP Precipitation dataset - a publicly available Zarr dataset\n",
103+
"url = \"https://ncsa.osn.xsede.org/Pangeo/pangeo-forge/gpcp-feedstock/gpcp.zarr\"\n",
104+
"\n",
105+
"# Get available variables\n",
106+
"variables = leafmap.zarr_variables(url, titiler_endpoint=endpoint)\n",
107+
"print(f\"Available variables: {variables}\")"
108+
]
109+
},
110+
{
111+
"cell_type": "markdown",
112+
"metadata": {},
113+
"source": [
114+
"### Get Dataset Information\n",
115+
"\n",
116+
"Use the `zarr_info` function to get detailed information about a Zarr dataset."
117+
]
118+
},
119+
{
120+
"cell_type": "code",
121+
"execution_count": null,
122+
"metadata": {},
123+
"outputs": [],
124+
"source": [
125+
"# Get info requires specifying a variable for titiler-xarray\n",
126+
"info = leafmap.zarr_info(url, variable=\"precip\", titiler_endpoint=endpoint)\n",
127+
"print(f\"Bounds: {info.get('bounds')}\")\n",
128+
"print(f\"CRS: {info.get('crs')}\")"
129+
]
130+
},
131+
{
132+
"cell_type": "markdown",
133+
"metadata": {},
134+
"source": [
135+
"### Get Dataset Bounds\n",
136+
"\n",
137+
"Use the `zarr_bounds` function to get the geographic bounds of a Zarr dataset."
138+
]
139+
},
140+
{
141+
"cell_type": "code",
142+
"execution_count": null,
143+
"metadata": {},
144+
"outputs": [],
145+
"source": [
146+
"bounds = leafmap.zarr_bounds(url, variable=\"precip\", titiler_endpoint=endpoint)\n",
147+
"print(f\"Bounds (minx, miny, maxx, maxy): {bounds}\")"
148+
]
149+
},
150+
{
151+
"cell_type": "markdown",
152+
"metadata": {},
153+
"source": [
154+
"## Visualizing Zarr Data with ipyleaflet backend\n",
155+
"\n",
156+
"The `add_zarr` method allows you to add Zarr datasets to the map. It requires:\n",
157+
"- A URL to the Zarr dataset\n",
158+
"- A variable name for multi-variable datasets\n",
159+
"- A titiler endpoint with xarray support\n",
160+
"\n",
161+
"**Note:** For datasets with a time dimension, the `time_index` parameter specifies which time step to display (default is 0, the first time step)."
162+
]
163+
},
164+
{
165+
"cell_type": "code",
166+
"execution_count": null,
167+
"metadata": {},
168+
"outputs": [],
169+
"source": [
170+
"m = leafmap.Map(center=[0, 0], zoom=1)\n",
171+
"m.add_zarr(\n",
172+
" url,\n",
173+
" variable=\"precip\",\n",
174+
" name=\"Precipitation\",\n",
175+
" colormap_name=\"blues\",\n",
176+
" rescale=\"0,20\",\n",
177+
" titiler_endpoint=endpoint,\n",
178+
" time_index=0, # Display first time step (default)\n",
179+
")\n",
180+
"m"
181+
]
182+
},
183+
{
184+
"cell_type": "markdown",
185+
"metadata": {},
186+
"source": [
187+
"### Displaying Different Time Steps\n",
188+
"\n",
189+
"You can change the `time_index` parameter to visualize different time steps."
190+
]
191+
},
192+
{
193+
"cell_type": "code",
194+
"execution_count": null,
195+
"metadata": {},
196+
"outputs": [],
197+
"source": [
198+
"m = leafmap.Map(center=[0, 0], zoom=1)\n",
199+
"m.add_zarr(\n",
200+
" url,\n",
201+
" variable=\"precip\",\n",
202+
" name=\"Precipitation (time=100)\",\n",
203+
" colormap_name=\"viridis\",\n",
204+
" rescale=\"0,20\",\n",
205+
" titiler_endpoint=endpoint,\n",
206+
" time_index=100, # Display 100th time step\n",
207+
")\n",
208+
"m"
209+
]
210+
},
211+
{
212+
"cell_type": "markdown",
213+
"metadata": {},
214+
"source": [
215+
"## Visualizing Zarr Data with MapLibre backend\n",
216+
"\n",
217+
"The `add_zarr` method is also available in the MapLibre backend."
218+
]
219+
},
220+
{
221+
"cell_type": "code",
222+
"execution_count": null,
223+
"metadata": {},
224+
"outputs": [],
225+
"source": [
226+
"import leafmap.maplibregl as leafmap"
227+
]
228+
},
229+
{
230+
"cell_type": "code",
231+
"execution_count": null,
232+
"metadata": {},
233+
"outputs": [],
234+
"source": [
235+
"m = leafmap.Map(center=[0, 0], zoom=1)\n",
236+
"m.add_zarr(\n",
237+
" url,\n",
238+
" variable=\"precip\",\n",
239+
" name=\"Precipitation\",\n",
240+
" colormap_name=\"viridis\",\n",
241+
" rescale=\"0,20\",\n",
242+
" titiler_endpoint=endpoint,\n",
243+
" time_index=0,\n",
244+
")\n",
245+
"m"
246+
]
247+
},
248+
{
249+
"cell_type": "markdown",
250+
"metadata": {},
251+
"source": [
252+
"## Using xarray to Explore Zarr Data\n",
253+
"\n",
254+
"You can also use xarray directly to explore Zarr datasets before visualization."
255+
]
256+
},
257+
{
258+
"cell_type": "code",
259+
"execution_count": null,
260+
"metadata": {},
261+
"outputs": [],
262+
"source": [
263+
"import xarray as xr\n",
264+
"\n",
265+
"# Open the GPCP precipitation dataset\n",
266+
"url = \"https://ncsa.osn.xsede.org/Pangeo/pangeo-forge/gpcp-feedstock/gpcp.zarr\"\n",
267+
"ds = xr.open_zarr(url)\n",
268+
"ds"
269+
]
270+
},
271+
{
272+
"cell_type": "code",
273+
"execution_count": null,
274+
"metadata": {},
275+
"outputs": [],
276+
"source": [
277+
"# List all data variables\n",
278+
"print(\"Data variables:\")\n",
279+
"for var in ds.data_vars:\n",
280+
" print(f\" - {var}: {ds[var].dims}\")"
281+
]
282+
},
283+
{
284+
"cell_type": "code",
285+
"execution_count": null,
286+
"metadata": {},
287+
"outputs": [],
288+
"source": [
289+
"# Get information about a specific variable\n",
290+
"ds[\"precip\"]"
291+
]
292+
},
293+
{
294+
"cell_type": "code",
295+
"execution_count": null,
296+
"metadata": {},
297+
"outputs": [],
298+
"source": [
299+
"# Show available time steps\n",
300+
"print(f\"Number of time steps: {len(ds.time)}\")\n",
301+
"print(f\"First time: {ds.time.values[0]}\")\n",
302+
"print(f\"Last time: {ds.time.values[-1]}\")"
303+
]
304+
},
305+
{
306+
"cell_type": "markdown",
307+
"metadata": {},
308+
"source": [
309+
"## Option 2: Using a Remote TiTiler-XArray Endpoint\n",
310+
"\n",
311+
"If you have access to a remote titiler-xarray endpoint (e.g., from titiler-eopf), you can use it directly:\n",
312+
"\n",
313+
"```python\n",
314+
"# Set the endpoint URL\n",
315+
"endpoint = \"https://your-titiler-xarray-endpoint.com\"\n",
316+
"\n",
317+
"m = leafmap.Map()\n",
318+
"m.add_zarr(\n",
319+
" url=\"https://example.com/data.zarr\",\n",
320+
" variable=\"temperature\",\n",
321+
" titiler_endpoint=endpoint,\n",
322+
")\n",
323+
"m\n",
324+
"```\n",
325+
"\n",
326+
"You can also set the endpoint as an environment variable:\n",
327+
"\n",
328+
"```python\n",
329+
"import os\n",
330+
"os.environ[\"TITILER_XARRAY_ENDPOINT\"] = \"https://your-titiler-xarray-endpoint.com\"\n",
331+
"```"
332+
]
333+
},
334+
{
335+
"cell_type": "markdown",
336+
"metadata": {},
337+
"source": [
338+
"## Summary\n",
339+
"\n",
340+
"Key functions for working with Zarr data in leafmap:\n",
341+
"\n",
342+
"- `leafmap.run_titiler_xarray()` - Start a local titiler-xarray server\n",
343+
"- `map.add_zarr()` - Add a Zarr dataset to the map\n",
344+
" - `time_index` parameter specifies which time step to display (default: 0)\n",
345+
"- `leafmap.zarr_variables()` - Get list of variables in a Zarr dataset\n",
346+
"- `leafmap.zarr_info()` - Get metadata about a Zarr dataset\n",
347+
"- `leafmap.zarr_bounds()` - Get geographic bounds of a Zarr dataset\n",
348+
"- `leafmap.zarr_statistics()` - Get statistics for a Zarr variable\n",
349+
"\n",
350+
"### Public Zarr Datasets for Testing\n",
351+
"\n",
352+
"Here are some publicly available Zarr datasets you can use:\n",
353+
"\n",
354+
"- **GPCP Precipitation**: `https://ncsa.osn.xsede.org/Pangeo/pangeo-forge/gpcp-feedstock/gpcp.zarr`"
355+
]
356+
}
357+
],
358+
"metadata": {
359+
"kernelspec": {
360+
"display_name": "geo",
361+
"language": "python",
362+
"name": "python3"
363+
},
364+
"language_info": {
365+
"codemirror_mode": {
366+
"name": "ipython",
367+
"version": 3
368+
},
369+
"file_extension": ".py",
370+
"mimetype": "text/x-python",
371+
"name": "python",
372+
"nbconvert_exporter": "python",
373+
"pygments_lexer": "ipython3",
374+
"version": "3.12.12"
375+
}
376+
},
377+
"nbformat": 4,
378+
"nbformat_minor": 4
379+
}

0 commit comments

Comments
 (0)