import xarray as xr
import holoviews as hv
import panel as pn
import hvplot.xarray
from holoviews import opts
pn.extension()
Data¶
In this notebook, we are going to load annual mean dataset of 2-m temperature from the ERA5 reanalysis that we preprocessed in Zarr format. Please see the preprocessing notebooks for the required steps.
rda_url = "https://data.rda.ucar.edu/"
annual_means = rda_url + "pythia_era5_24/annual_means/"
xrds = xr.open_dataset(annual_means + "temp_2m_annual_1940_2023.zarr", engine="zarr")
xrds
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/mapping.py:155, in FSMap.__getitem__(self, key, default)
154 try:
--> 155 result = self.fs.cat(k)
156 except self.missing_exceptions as exc:
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/asyn.py:118, in sync_wrapper.<locals>.wrapper(*args, **kwargs)
117 self = obj or args[0]
--> 118 return sync(self.loop, func, *args, **kwargs)
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/asyn.py:103, in sync(loop, func, timeout, *args, **kwargs)
102 elif isinstance(return_result, BaseException):
--> 103 raise return_result
104 else:
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/asyn.py:56, in _runner(event, coro, result, timeout)
55 try:
---> 56 result[0] = await coro
57 except Exception as ex:
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/asyn.py:464, in AsyncFileSystem._cat(self, path, recursive, on_error, batch_size, **kwargs)
463 if ex:
--> 464 raise ex
465 if (
466 len(paths) > 1
467 or isinstance(path, list)
468 or paths[0] != self._strip_protocol(path)
469 ):
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/asyn.py:244, in _run_coros_in_chunks.<locals>._run_coro(coro, i)
243 try:
--> 244 return await asyncio.wait_for(coro, timeout=timeout), i
245 except Exception as e:
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/asyncio/tasks.py:507, in wait_for(fut, timeout)
506 async with timeouts.timeout(timeout):
--> 507 return await fut
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/implementations/http.py:246, in HTTPFileSystem._cat_file(self, url, start, end, **kwargs)
245 out = await r.read()
--> 246 self._raise_not_found_for_status(r, url)
247 return out
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/implementations/http.py:228, in HTTPFileSystem._raise_not_found_for_status(self, response, url)
227 if response.status == 404:
--> 228 raise FileNotFoundError(url)
229 response.raise_for_status()
FileNotFoundError: https://data.rda.ucar.edu/pythia_era5_24/annual_means/temp_2m_annual_1940_2023.zarr/.zmetadata
The above exception was the direct cause of the following exception:
KeyError Traceback (most recent call last)
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/zarr/storage.py:1446, in FSStore.__getitem__(self, key)
1445 try:
-> 1446 return self.map[key]
1447 except self.exceptions as e:
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/fsspec/mapping.py:159, in FSMap.__getitem__(self, key, default)
158 return default
--> 159 raise KeyError(key) from exc
160 return result
KeyError: '.zmetadata'
The above exception was the direct cause of the following exception:
KeyError Traceback (most recent call last)
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/xarray/backends/zarr.py:1862, in _get_open_params(store, mode, synchronizer, group, consolidated, consolidate_on_close, chunk_store, storage_options, zarr_version, use_zarr_fill_value_as_mask, zarr_format)
1861 try:
-> 1862 zarr_root_group = zarr.open_consolidated(store, **open_kwargs)
1863 except (ValueError, KeyError):
1864 # ValueError in zarr-python 3.x, KeyError in 2.x.
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/zarr/convenience.py:1362, in open_consolidated(store, metadata_key, mode, **kwargs)
1361 # setup metadata store
-> 1362 meta_store = ConsolidatedStoreClass(store, metadata_key=metadata_key)
1364 # pass through
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/zarr/storage.py:3045, in ConsolidatedMetadataStore.__init__(self, store, metadata_key)
3044 # retrieve consolidated metadata
-> 3045 meta = json_loads(self.store[metadata_key])
3047 # check format of consolidated metadata
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/zarr/storage.py:1448, in FSStore.__getitem__(self, key)
1447 except self.exceptions as e:
-> 1448 raise KeyError(key) from e
KeyError: '.zmetadata'
During handling of the above exception, another exception occurred:
GroupNotFoundError Traceback (most recent call last)
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/xarray/backends/zarr.py:1866, in _get_open_params(store, mode, synchronizer, group, consolidated, consolidate_on_close, chunk_store, storage_options, zarr_version, use_zarr_fill_value_as_mask, zarr_format)
1865 try:
-> 1866 zarr_root_group = zarr.open_group(store, **open_kwargs)
1867 emit_user_level_warning(
1868 "Failed to open Zarr store with consolidated metadata, "
1869 "but successfully read with non-consolidated metadata. "
(...) 1879 RuntimeWarning,
1880 )
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/zarr/hierarchy.py:1578, in open_group(store, mode, cache_attrs, synchronizer, path, chunk_store, storage_options, zarr_version, meta_array)
1577 raise ContainsArrayError(path)
-> 1578 raise GroupNotFoundError(path)
1580 elif mode == "w":
GroupNotFoundError: group not found at path ''
The above exception was the direct cause of the following exception:
FileNotFoundError Traceback (most recent call last)
Cell In[2], line 3
1 rda_url = "https://data.rda.ucar.edu/"
2 annual_means = rda_url + "pythia_era5_24/annual_means/"
----> 3 xrds = xr.open_dataset(annual_means + "temp_2m_annual_1940_2023.zarr", engine="zarr")
4 xrds
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/xarray/backends/api.py:760, in open_dataset(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
748 decoders = _resolve_decoders_kwargs(
749 decode_cf,
750 open_backend_dataset_parameters=backend.open_dataset_parameters,
(...) 756 decode_coords=decode_coords,
757 )
759 overwrite_encoded_chunks = kwargs.pop("overwrite_encoded_chunks", None)
--> 760 backend_ds = backend.open_dataset(
761 filename_or_obj,
762 drop_variables=drop_variables,
763 **decoders,
764 **kwargs,
765 )
766 ds = _dataset_from_backend_dataset(
767 backend_ds,
768 filename_or_obj,
(...) 779 **kwargs,
780 )
781 return ds
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/xarray/backends/zarr.py:1654, in ZarrBackendEntrypoint.open_dataset(self, filename_or_obj, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, use_cftime, decode_timedelta, group, mode, synchronizer, consolidated, chunk_store, storage_options, zarr_version, zarr_format, store, engine, use_zarr_fill_value_as_mask, cache_members)
1652 filename_or_obj = _normalize_path(filename_or_obj)
1653 if not store:
-> 1654 store = ZarrStore.open_group(
1655 filename_or_obj,
1656 group=group,
1657 mode=mode,
1658 synchronizer=synchronizer,
1659 consolidated=consolidated,
1660 consolidate_on_close=False,
1661 chunk_store=chunk_store,
1662 storage_options=storage_options,
1663 zarr_version=zarr_version,
1664 use_zarr_fill_value_as_mask=None,
1665 zarr_format=zarr_format,
1666 cache_members=cache_members,
1667 )
1669 store_entrypoint = StoreBackendEntrypoint()
1670 with close_on_error(store):
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/xarray/backends/zarr.py:714, in ZarrStore.open_group(cls, store, mode, synchronizer, group, consolidated, consolidate_on_close, chunk_store, storage_options, append_dim, write_region, safe_chunks, align_chunks, zarr_version, zarr_format, use_zarr_fill_value_as_mask, write_empty, cache_members)
688 @classmethod
689 def open_group(
690 cls,
(...) 707 cache_members: bool = True,
708 ):
709 (
710 zarr_group,
711 consolidate_on_close,
712 close_store_on_close,
713 use_zarr_fill_value_as_mask,
--> 714 ) = _get_open_params(
715 store=store,
716 mode=mode,
717 synchronizer=synchronizer,
718 group=group,
719 consolidated=consolidated,
720 consolidate_on_close=consolidate_on_close,
721 chunk_store=chunk_store,
722 storage_options=storage_options,
723 zarr_version=zarr_version,
724 use_zarr_fill_value_as_mask=use_zarr_fill_value_as_mask,
725 zarr_format=zarr_format,
726 )
728 return cls(
729 zarr_group,
730 mode,
(...) 739 cache_members=cache_members,
740 )
File ~/micromamba/envs/ERA5_interactive/lib/python3.13/site-packages/xarray/backends/zarr.py:1882, in _get_open_params(store, mode, synchronizer, group, consolidated, consolidate_on_close, chunk_store, storage_options, zarr_version, use_zarr_fill_value_as_mask, zarr_format)
1867 emit_user_level_warning(
1868 "Failed to open Zarr store with consolidated metadata, "
1869 "but successfully read with non-consolidated metadata. "
(...) 1879 RuntimeWarning,
1880 )
1881 except missing_exc as err:
-> 1882 raise FileNotFoundError(
1883 f"No such file or directory: '{store}'"
1884 ) from err
1886 # but the user should still receive a DataTree whose root is the group they asked for
1887 if group and group != "/":
FileNotFoundError: No such file or directory: 'https://data.rda.ucar.edu/pythia_era5_24/annual_means/temp_2m_annual_1940_2023.zarr'
# Select the time range of interest
xrds = xrds.sel(time=slice('2017-01-01', '2023-12-31'))
xrds.load();
Panel Widgets¶
Panel provides a variety of widgets that can be used to build interactive dashboards. In this notebook, we are going to use some of these widgets. For the complete list of widgets, please see the Panel documentation.
The panel widgets that we are using are:
pn.widgets.Select
for selecting the variablepn.widgets.DatePicker
for selecting the datepn.widgets.Player
for making time series animations
w_var = pn.widgets.Select(name="Data Variable", options=list(xrds.data_vars))
dataset_controls = pn.WidgetBox(
"## Dataset Controls",
w_var,
)
dataset_controls
Now, let’s create dropdown widgets for selecting the colormap and plot type.
w_cmap = pn.widgets.Select(name="Colormap", options=["inferno", "plasma", "coolwarm"])
w_plot_type = pn.widgets.Select(
name="Plot Type", options=["Color Plot", "Contour"]
)
plot_controls = pn.WidgetBox(
"## Plot Controls",
w_plot_type,
w_cmap,
)
plot_controls
Now, let’s put together all the controls and the plot in a panel layout using pn.Row
and pn.Column
:
controls = pn.Column(dataset_controls, plot_controls)
controls
w_player = pn.widgets.Player(
value=0,
start=0,
end=len(xrds.time) - 1,
name="Year",
loop_policy="loop",
interval=300,
align="center",
width_policy="fit",
)
w_player
Plotting Function¶
def plot_ds(time, var, cmap, plot_type):
clim = (xrds[var].values.min(), xrds[var].values.max())
if plot_type == "Color Plot":
return (
xrds[var]
.isel(time=time)
.hvplot(
cmap=cmap,
title=str(f"{var} year {time}"),
clim=clim,
dynamic=False,
rasterize=True,
precompute=True,
)
.opts(framewise=False)
)
elif plot_type == "Contour":
return (
xrds[var]
.isel(time=time)
.hvplot.contour(
cmap=cmap,
dynamic=False,
rasterize=True,
title=str(f"{var} Year: {time}"),
clim=clim,
precompute=True,
)
.opts(framewise=False)
)
app = pn.Row(
controls,
pn.Column(
pn.panel(
hv.DynamicMap(
pn.bind(
plot_ds,
time=w_player,
var=w_var,
cmap=w_cmap,
plot_type=w_plot_type,
)
)
),
w_player,
),
).servable()
app
Please note how the above dashboard is servable. You can deploy the dashboard by running the following command in the terminal:
panel serve --show 04_dashboard.ipynb --allow-websocket-origin=projectpythia.2i2c.cloud
This will open a new tab in your default web browser with the dashboard. The allow websocket origin flag is required to allow traffic to flow to the site. This should be update to reflect the base URL where the application is launched. A wildcard can be used, *
, to allow traffic from any site to connect.
On the Project Pythia 2i2c hosted JupyterHub the link to use to access the panel serve command is https://
- C3S. (2018). ERA5 hourly data on single levels from 1940 to present. Copernicus Climate Change Service (C3S) Climate Data Store (CDS). 10.24381/CDS.ADBB2D47