{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", " \n", " \n", " \n", " \n", " \n", "
\n", " \"Project\n", " \n", " \"ECMWF\n", " \n", " \"Google\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 02_InteractiveVisualization Part 1: Geoviews" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Overview\n", "A team at [Google Research & Cloud](https://research.google/) are making parts of the [ECMWF Reanalysis version 5](https://www.ecmwf.int/en/forecasts/dataset/ecmwf-reanalysis-v5) (aka **ERA-5**) accessible in a [Analysis Ready, Cloud Optimized](https://www.frontiersin.org/articles/10.3389/fclim.2021.782909/full) (aka **ARCO**) format.\n", "\n", "In this notebook, we will do the following:\n", "\n", "1. Access the [ERA-5 ARCO](https://github.com/google-research/arco-era5) catalog\n", "1. Select a particular dataset and variable from the catalog\n", "1. Convert the data from Gaussian to Cartesian coordinates\n", "1. Plot a map of sea-level pressure contours and 2-meter temperature mesh using Geoviews." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Prerequisites\n", "\n", "| Concepts | Importance | Notes |\n", "| --- | --- | --- |\n", "| [Cartopy](https://foundations.projectpythia.org/core/cartopy/cartopy.html) | Necessary | |\n", "| [Xarray](https://foundations.projectpythia.org/core/xarray) | Necessary | |\n", "| [Geoviews] | Necessary | |\n", "\n", "\n", "- **Time to learn**: 30 minutes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import fsspec\n", "import xarray as xr\n", "import matplotlib.pyplot as plt\n", "import cartopy.crs as ccrs\n", "import cartopy.feature as cfeature\n", "from metpy import calc as mpcalc\n", "from metpy.units import units\n", "import metpy\n", "import scipy.spatial\n", "import numpy as np\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Access the ARCO ERA-5 catalog on Google Cloud" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's open the **37-level isobaric surfaces reanalysis** Zarr file" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "reanalysis = xr.open_zarr(\n", " 'gs://gcp-public-data-arco-era5/ar/1959-2022-full_37-1h-0p25deg-chunk-1.zarr-v2', \n", " chunks={'time': 48},\n", " consolidated=True,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "reanalysis" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "geop = reanalysis.geopotential\n", "temp = reanalysis.temperature" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "geop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Select time and level ranges from the dataset." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "geopSub1 = geop.sel(time=slice('1993-03-13T18:00:00','1993-03-14T00:00:00'), level=500).compute()\n", "tempSub1 = temp.sel(time=slice('1993-03-13T18:00:00','1993-03-14T00:00:00'), level=850).compute()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convert to dam and deg C." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "Z = mpcalc.geopotential_to_height(geopSub1).metpy.convert_units('dam')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "T = tempSub1.metpy.convert_units('degC')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Z" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "T" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot the data using Matplotlib" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "projData = ccrs.PlateCarree()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "lons, lats = Z.longitude, Z.latitude" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "tLevels = np.arange(-45,39,3)\n", "zLevels = np.arange(468, 606, 6)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "res = '110m'\n", "dpi = 100\n", "fig = plt.figure(figsize=(2048/dpi, 1024/dpi))\n", "ax = plt.subplot(1,1,1,projection=projData, frameon=False)\n", "\n", "ax.add_feature(cfeature.COASTLINE.with_scale(res), edgecolor='brown', linewidth=2.5)\n", "ax.add_feature(cfeature.BORDERS.with_scale(res), edgecolor='brown', linewidth=2.5)\n", "ax.add_feature(cfeature.STATES.with_scale(res), edgecolor='brown')\n", "\n", "# Temperature (T) contour fills\n", "\n", "# Note we don't need the transform argument since the map/data projections are the same, but we'll leave it in\n", "CF = ax.contourf(lons,lats,T,levels=tLevels,cmap=plt.get_cmap('coolwarm'), extend='both', transform=projData) \n", "\n", "# Height (Z) contour lines\n", "CL = ax.contour(lons,lats,Z,zLevels,linewidths=1.25,colors='yellow', transform=projData)\n", "ax.clabel(CL, inline_spacing=0.2, fontsize=8, fmt='%.0f')\n", "fig.tight_layout(pad=.01)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "res = '110m'\n", "dpi = 100\n", "fig = plt.figure(figsize=(2048/dpi, 1024/dpi))\n", "ax = plt.subplot(1,1,1,projection=projData, frameon=False)\n", "\n", "ax.add_feature(cfeature.COASTLINE.with_scale(res), edgecolor='brown', linewidth=2.5)\n", "ax.add_feature(cfeature.BORDERS.with_scale(res), edgecolor='brown', linewidth=2.5)\n", "ax.add_feature(cfeature.STATES.with_scale(res), edgecolor='brown')\n", "\n", "# Temperature (T) contour fills\n", "\n", "# Note we don't need the transform argument since the map/data projections are the same, but we'll leave it in\n", "CF = ax.contourf(lons,lats,T.isel(time=0),levels=tLevels,cmap=plt.get_cmap('coolwarm'), extend='both', transform=projData) \n", "\n", "# Height (Z) contour lines\n", "CL = ax.contour(lons,lats,Z.isel(time=0),zLevels,linewidths=1.25,colors='yellow', transform=projData)\n", "ax.clabel(CL, inline_spacing=0.2, fontsize=8, fmt='%.0f')\n", "fig.tight_layout(pad=.01)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "lonW, lonE, latS, latN = -130, -60, 20, 55 " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "lonRange = np.arange(lonW, lonE + 0.1, 0.25)\n", "latRange = np.arange(latS, latN + 0.1, 0.25)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "ZSub2 = Z.sel(latitude = latRange, longitude = lonRange)\n", "TSub2 = T.sel(latitude = latRange, longitude = lonRange)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "projMap = ccrs." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "res = '50m'\n", "dpi = 100\n", "fig = plt.figure(figsize=(2048/dpi, 1024/dpi))\n", "ax = plt.subplot(1,1,1,projection=, frameon=False)\n", "\n", "ax.add_feature(cfeature.COASTLINE.with_scale(res), edgecolor='brown', linewidth=2.5)\n", "ax.add_feature(cfeature.BORDERS.with_scale(res), edgecolor='brown', linewidth=2.5)\n", "ax.add_feature(cfeature.STATES.with_scale(res), edgecolor='brown')\n", "\n", "# Temperature (T) contour fills\n", "\n", "# Note we don't need the transform argument since the map/data projections are the same, but we'll leave it in\n", "CF = ax.contourf(lons,lats,T.isel(time=0),levels=tLevels,cmap=plt.get_cmap('coolwarm'), extend='both', transform=projData) \n", "\n", "# Height (Z) contour lines\n", "CL = ax.contour(lons,lats,Z.isel(time=0),zLevels,linewidths=1.25,colors='yellow', transform=projData)\n", "ax.clabel(CL, inline_spacing=0.2, fontsize=8, fmt='%.0f')\n", "fig.tight_layout(pad=.01)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 September 2023 Environment", "language": "python", "name": "sep23" }, "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.5" }, "nbdime-conflicts": { "local_diff": [ { "diff": [ { "diff": [ { "key": 0, "op": "addrange", "valuelist": [ "Python 3" ] }, { "key": 0, "length": 1, "op": "removerange" } ], "key": "display_name", "op": "patch" } ], "key": "kernelspec", "op": "patch" } ], "remote_diff": [ { "diff": [ { "diff": [ { "key": 0, "op": "addrange", "valuelist": [ "Python3" ] }, { "key": 0, "length": 1, "op": "removerange" } ], "key": "display_name", "op": "patch" } ], "key": "kernelspec", "op": "patch" } ] }, "toc-autonumbering": false }, "nbformat": 4, "nbformat_minor": 4 }