diff --git a/docs/guide/jupyterlab/notebooks/Visualise_data_with_Lexcube.ipynb b/docs/guide/jupyterlab/notebooks/Visualise_data_with_Lexcube.ipynb new file mode 100644 index 00000000..5e46a8d2 --- /dev/null +++ b/docs/guide/jupyterlab/notebooks/Visualise_data_with_Lexcube.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "21584fb9-bcc6-4af8-957f-d66d57efab44", + "metadata": { + "tags": [] + }, + "source": [ + "## How to visualise data with Lexcube in your workspace\n", + "### A DeepESDL example notebook \n", + "\n", + "![Demo GIF](/img/lexcube/lexcube-demo.gif)\n", + "\n", + "This notebook demonstrates how to use Lexcube in Jupyter to visualize and explore any 3D data from Xarray (`xarray.Dataset`) or Numpy (`np.ndarray`) as 3D interactive data cubes, from any source (S3, HTTP, local file, in-memory).\n", + "\n", + "For more info on the visualization, please refer to the [Lexcube](/guide/lexcube-viewer) page in the documentation.\n", + "For the complete API of the Python package, please refer to the [GitHub page](https://github.com/msoechting/lexcube).\n" + ] + }, + { + "cell_type": "markdown", + "id": "06646bd3-519e-4bc8-b882-0e37b260d110", + "metadata": {}, + "source": [ + "First, we load a `xarray.Dataset` which we will visualise with Lexcube. For this example, we will use the DeepESDL Earth System Data Cube data set and load air temperature data. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee28ac94-f9b7-4cc8-bd1e-27ee2e00ac2d", + "metadata": {}, + "outputs": [], + "source": [ + "import xarray\n", + "import lexcube" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f630bce6", + "metadata": {}, + "outputs": [], + "source": [ + "### Option 1: Load data from the public S3 bucket\n", + "from xcube.core.store import new_data_store\n", + "import os\n", + "store = new_data_store(\"s3\", root=\"deep-esdl-public\", storage_options=dict(anon=True))\n", + "ds = store.open_data('esdc-8d-0.25deg-1x720x1440-3.0.1.zarr')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb47fe4c-6f8b-40a7-8464-25d13c9e2ecf", + "metadata": {}, + "outputs": [], + "source": [ + "### Option 2: Load data from public HTTP - works also outside of DeepESDL\n", + "esdc_url = \"https://data.rsc4earth.de/download/EarthSystemDataCube/v3.0.2/esdc-8d-0.25deg-256x128x128-3.0.2.zarr/\"\n", + "ds = xarray.open_dataset(esdc_url, chunks={}, engine=\"zarr\")" + ] + }, + { + "cell_type": "markdown", + "id": "c7ab42d5-b7a6-457b-bd37-31b680e090ed", + "metadata": {}, + "source": [ + "Now we can open the interactive visualization. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71e05ba2-a01a-457a-bf46-67145fcc8485", + "metadata": {}, + "outputs": [], + "source": [ + "data_selection = ds[\"air_temperature_2m\"][256:512]\n", + "w = lexcube.Cube3DWidget(data_selection, cmap=\"thermal\")\n", + "w.plot()" + ] + }, + { + "cell_type": "markdown", + "id": "636c021d-f5f3-41a3-9c6a-30e1508b00f5", + "metadata": {}, + "source": [ + "Zoom and drag on the cube to move within the data cube. Click and drag on the black background to rotate the cube.\n", + "\n", + "#### Customize the Visualization\n", + "\n", + "1. We can load any GeoJSON, e.g., the Natural Earth administrative boundaries and overlay them on the cube." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "822f6c56", + "metadata": {}, + "outputs": [], + "source": [ + "ne_url = \"https://github.com/nvkelso/natural-earth-vector/raw/refs/heads/master/geojson/ne_50m_admin_0_countries.geojson\"\n", + "w.overlay_geojson(ne_url, \"black\")" + ] + }, + { + "cell_type": "markdown", + "id": "40b19ec3", + "metadata": {}, + "source": [ + "2. Adjust the colormap. *This can also be done in the visualization itself by clicking on the color gradient.*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "724141e5", + "metadata": {}, + "outputs": [], + "source": [ + "w.cmap = \"thermal\" # any matplotlib colormap\n", + "w.vmin = -20\n", + "w.vmax = 30" + ] + }, + { + "cell_type": "markdown", + "id": "7ba21d4e", + "metadata": {}, + "source": [ + "3. Save a static PNG figure. *For creating MP4/GIF videos, select the \"Record\" option at the bottom of the animation menu (\"play\" button).*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df535a6e", + "metadata": {}, + "outputs": [], + "source": [ + "w.savefig(fname=\"esdc-at2m-cube.png\", include_ui=True)" + ] + }, + { + "cell_type": "markdown", + "id": "86424140", + "metadata": {}, + "source": [ + "4. Create a paper template to craft your own paper cube." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "205a9cd4", + "metadata": {}, + "outputs": [], + "source": [ + "w.save_print_template()" + ] + }, + { + "cell_type": "markdown", + "id": "eb3b5a7b", + "metadata": {}, + "source": [ + "5. Get the currently visualized data subset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea396f89", + "metadata": {}, + "outputs": [], + "source": [ + "currently_visualized_cube = w.get_current_cube_selection()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "deepesdl-xcube-1.7.0", + "language": "python", + "name": "conda-env-deepesdl-xcube-1.7.0-py" + }, + "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.11.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/guide/lexcube-viewer.md b/docs/guide/lexcube-viewer.md index e69de29b..eb80ffb9 100644 --- a/docs/guide/lexcube-viewer.md +++ b/docs/guide/lexcube-viewer.md @@ -0,0 +1,44 @@ +# Lexcube: Interactive 3D Data Cube Visualization + +![Demo GIF](../img/lexcube/lexcube-demo.gif) + +Lexcube is an interactive 3D data cube visualization tool. It exists in two versions: + +* [Lexcube.org](https://www.lexcube.org): Hosted online, very fast visualization, no setup required. + +* [Lexcube for Jupyter](https://github.com/msoechting/lexcube): Open-source Python package for Jupyter notebooks, visualize any data. + + +You can interact with the cube in multiple ways: zoom in/out or pan around in the data set, and rotate the cube around. With the "play" button, you can animate through a selected dimension (typically time) and optionally record it as a MP4, GIF or WebM video. + +To give geospatial context, GeoJSON vectors can be overlaid over the cube. On Lexcube.org, Natural Earth administrative boundaries are automatically displayed. In Lexcube for Jupyter, any user-provided GeoJSON can be visualized. + +At any point, you can create a print template from your currently visualized cube for crafting a corresponding paper cube: + +![Paper cube crafting demonstration](../img/lexcube/lexcube-print-template.png) + +## Lexcube.org + +At [Lexcube.org](https://www.lexcube.org), users can explore a selection of data sets from DeepESDL and other data providers. This works on all modern devices, including smartphones and tablets, making the data particularly accessible without any setup or coding knowledge necessary. + +In the data selection menu (top right, "database/cog" icon), the currently visualized data set and parameter can be selected. For each parameter, the difference in comparison to the smoothed mean seasonal cycle at the respective day-of-year can be explored ("anomalies" in the parameter selection). + + +Currently available data sets: + +* Earth System Data Cube v2.1.1 (*72 parameters*) + +* Earth System Data Cube v3.0.2 (*42 parameters*) + +* ERA5 Specific Humidity (*1 parameter*) + +* Spectral Indices from Sentinel-2 in Hainich, Germany (*97 parameters*) + +* EAC4 CAMS global reanalysis (*33 parameters*) + + +## Lexcube for Jupyter + +[Lexcube for Jupyter](https://github.com/msoechting/lexcube) is an open-source Python package that allows interactive 3D data cube visualization in Jupyter notebooks. This means that any 3D Xarray or Numpy data set from any source (in-memory, local file, remote HTTP/S3/...) can be visualized and explored within the notebook (see demo GIF at the top). The API allows to customize the colormaps, overlay GeoJSON and make precise data selections. For a more detailed description of the capabilities, refer to the [README](https://github.com/msoechting/lexcube?tab=readme-ov-file) on Github. + +To get started using Lexcube for Jupyter, please refer to the ["Visualize data with Lexcube"](/guide/jupyterlab/notebooks/Visualise_data_with_Lexcube/) example notebook. \ No newline at end of file diff --git a/docs/img/lexcube/lexcube-demo.gif b/docs/img/lexcube/lexcube-demo.gif new file mode 100644 index 00000000..4a248379 Binary files /dev/null and b/docs/img/lexcube/lexcube-demo.gif differ diff --git a/docs/img/lexcube/lexcube-print-template.png b/docs/img/lexcube/lexcube-print-template.png new file mode 100644 index 00000000..69ecd32a Binary files /dev/null and b/docs/img/lexcube/lexcube-print-template.png differ diff --git a/mkdocs.yml b/mkdocs.yml index e248c015..792ad761 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,6 +25,7 @@ nav: - Overview: guide/jupyterlab/notebooks/index.md - guide/jupyterlab/notebooks/Access_public_cubes.ipynb - guide/jupyterlab/notebooks/Visualise_data_with_xcube_viewer.ipynb + - guide/jupyterlab/notebooks/Visualise_data_with_Lexcube.ipynb - guide/jupyterlab/notebooks/Append_to_existing_cube.ipynb - guide/jupyterlab/notebooks/Chunking_of_datasets.ipynb - guide/jupyterlab/notebooks/Create_Atmospheric_Cubes_CDS.ipynb diff --git a/notebooks/generic-notebooks/Visualise_data_with_Lexcube.ipynb b/notebooks/generic-notebooks/Visualise_data_with_Lexcube.ipynb new file mode 100644 index 00000000..7cf9b8c2 --- /dev/null +++ b/notebooks/generic-notebooks/Visualise_data_with_Lexcube.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "21584fb9-bcc6-4af8-957f-d66d57efab44", + "metadata": { + "tags": [] + }, + "source": [ + "## How to visualise data with Lexcube in your workspace\n", + "### A DeepESDL example notebook \n", + "\n", + "![Demo GIF](/img/lexcube/lexcube-demo.gif)\n", + "\n", + "This notebook demonstrates how to use Lexcube in Jupyter to visualize and explore any 3D data from Xarray (`xarray.Dataset`) or Numpy (`np.ndarray`) as 3D interactive data cubes, from any source (S3, HTTP, local file, in-memory).\n", + "\n", + "For more info on the visualization, please refer to the [Lexcube](https://deepesdl.github.io/deepesdl-doc/guide/lexcube-viewer/) page in the documentation.\n", + "For the complete API of the Python package, please refer to the [GitHub page](https://github.com/msoechting/lexcube).\n" + ] + }, + { + "cell_type": "markdown", + "id": "06646bd3-519e-4bc8-b882-0e37b260d110", + "metadata": {}, + "source": [ + "First, we load a `xarray.Dataset` which we will visualise with Lexcube. For this example, we will use the DeepESDL Earth System Data Cube data set and load air temperature data. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee28ac94-f9b7-4cc8-bd1e-27ee2e00ac2d", + "metadata": {}, + "outputs": [], + "source": [ + "import xarray\n", + "import lexcube" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f630bce6", + "metadata": {}, + "outputs": [], + "source": [ + "### Option 1: Load data from the public S3 bucket\n", + "from xcube.core.store import new_data_store\n", + "import os\n", + "store = new_data_store(\"s3\", root=\"deep-esdl-public\", storage_options=dict(anon=True))\n", + "ds = store.open_data('esdc-8d-0.25deg-1x720x1440-3.0.1.zarr')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb47fe4c-6f8b-40a7-8464-25d13c9e2ecf", + "metadata": {}, + "outputs": [], + "source": [ + "### Option 2: Load data from public HTTP - works also outside of DeepESDL\n", + "esdc_url = \"https://data.rsc4earth.de/download/EarthSystemDataCube/v3.0.2/esdc-8d-0.25deg-256x128x128-3.0.2.zarr/\"\n", + "ds = xarray.open_dataset(esdc_url, chunks={}, engine=\"zarr\")" + ] + }, + { + "cell_type": "markdown", + "id": "c7ab42d5-b7a6-457b-bd37-31b680e090ed", + "metadata": {}, + "source": [ + "Now we can open the interactive visualization. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71e05ba2-a01a-457a-bf46-67145fcc8485", + "metadata": {}, + "outputs": [], + "source": [ + "data_selection = ds[\"air_temperature_2m\"][256:512]\n", + "w = lexcube.Cube3DWidget(data_selection, cmap=\"thermal\")\n", + "w.plot()" + ] + }, + { + "cell_type": "markdown", + "id": "636c021d-f5f3-41a3-9c6a-30e1508b00f5", + "metadata": {}, + "source": [ + "Zoom and drag on the cube to move within the data cube. Click and drag on the black background to rotate the cube.\n", + "\n", + "#### Customize the Visualization\n", + "\n", + "1. We can load any GeoJSON, e.g., the Natural Earth administrative boundaries and overlay them on the cube." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "822f6c56", + "metadata": {}, + "outputs": [], + "source": [ + "ne_url = \"https://github.com/nvkelso/natural-earth-vector/raw/refs/heads/master/geojson/ne_50m_admin_0_countries.geojson\"\n", + "w.overlay_geojson(ne_url, \"black\")" + ] + }, + { + "cell_type": "markdown", + "id": "40b19ec3", + "metadata": {}, + "source": [ + "2. Adjust the colormap. *This can also be done in the visualization itself by clicking on the color gradient.*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "724141e5", + "metadata": {}, + "outputs": [], + "source": [ + "w.cmap = \"thermal\" # any matplotlib colormap\n", + "w.vmin = -20\n", + "w.vmax = 30" + ] + }, + { + "cell_type": "markdown", + "id": "7ba21d4e", + "metadata": {}, + "source": [ + "3. Save a static PNG figure. *For creating MP4/GIF videos, select the \"Record\" option at the bottom of the animation menu (\"play\" button).*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df535a6e", + "metadata": {}, + "outputs": [], + "source": [ + "w.savefig(fname=\"esdc-at2m-cube.png\", include_ui=True)" + ] + }, + { + "cell_type": "markdown", + "id": "86424140", + "metadata": {}, + "source": [ + "4. Create a paper template to craft your own paper cube." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "205a9cd4", + "metadata": {}, + "outputs": [], + "source": [ + "w.save_print_template()" + ] + }, + { + "cell_type": "markdown", + "id": "eb3b5a7b", + "metadata": {}, + "source": [ + "5. Get the currently visualized data subset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea396f89", + "metadata": {}, + "outputs": [], + "source": [ + "currently_visualized_cube = w.get_current_cube_selection()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "deepesdl-xcube-1.7.0", + "language": "python", + "name": "conda-env-deepesdl-xcube-1.7.0-py" + }, + "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.11.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}