{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "GeoViews is designed to make full use of multidimensional gridded datasets stored in netCDF or other common formats, via the xarray and iris interfaces in HoloViews. This notebook will demonstrate how to load data using both of these data backends, along with some of their individual quirks. The data used in this notebook was originally shipped as part of the [``SciTools/iris-sample-data``](https://github.com/SciTools/iris-sample-data) repository, but a smaller netCDF file is included as part of the GeoViews so that it can be used with xarray as well." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import xarray as xr\n", "import holoviews as hv\n", "import geoviews as gv\n", "import geoviews.feature as gf\n", "from cartopy import crs\n", "\n", "hv.extension('matplotlib', 'bokeh')\n", "\n", "#conda install -c ioam -c conda-forge holoviews geoviews" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setting some notebook-wide options" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's start by setting some normalization options (discussed below) and always enable colorbars for the elements we will be displaying:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%opts Image {+framewise} [colorbar=True] Curve [xrotation=60]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can see that it is easy to set global defaults for a project, allowing any suitable settings to be made into a default on a per-element-type basis. Now let's specify the maximum number of frames we will be displaying:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%output max_frames=1000 " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
When working on a live server append ``widgets='live'`` to the line above for greatly improved performance and memory usage
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading our data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this notebook we will primarily be working with xarray, but we will also load the same data using iris so that we can demonstrate that the two data backends are nearly equivalent.\n", "\n", "#### XArray\n", "\n", "As a first step we simply load the data using the ``open_dataset`` method xarray provides and have a look at the repr to get an overview what is in this dataset:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xr_ensemble = xr.open_dataset('/home/pangeo/data/ensemble.nc')\n", "xr_ensemble" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "kdims = ['time', 'longitude', 'latitude']\n", "vdims = ['surface_temperature']\n", "\n", "xr_dataset = gv.Dataset(xr_ensemble, kdims=kdims, vdims=vdims, crs=crs.PlateCarree())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(repr(xr_dataset))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "print(\"XArray time type: %s\" % xr_dataset.get_dimension_type('time'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To improve the formatting of dates on the xarray dataset we can set the formatter for datetime64 types:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hv.Dimension.type_formatters[np.datetime64] = '%Y-%m-%d'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `Dataset` object is not yet visualizable, because we have not chosen which dimensions to map onto which axes of a plot.\n", "\n", "# A Simple example\n", "\n", "To visualize the datasets, in a single line of code we can specify that we want to view it as a collection of Images indexed by longitude and latitude (a HoloViews ``HoloMap`` of ``gv.Image`` elements):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xr_dataset.to(gv.Image, ['longitude', 'latitude'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can see that the `time` dimension was automatically mapped to a slider, because we did not map it onto one of the other available dimensions (x, y, or color, in this case). You can drag the slider to view the surface temperature at different times. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let us load the pre-industrial air temperature:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "air_temperature = gv.Dataset(xr.open_dataset('/home/pangeo/data/pre-industrial.nc'), kdims=['longitude', 'latitude'],\n", " group='Pre-industrial air temperature', vdims=['air_temperature'],\n", " crs=crs.PlateCarree())\n", "air_temperature" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we have the ``air_temperature`` available over ``longitude`` and ``latitude`` but *not* the ``time`` dimensions. As a result, this cube is a single frame (at right below) when visualized as a temperature map." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(xr_dataset.to.image(['longitude', 'latitude'])+\n", " air_temperature.to.image(['longitude', 'latitude']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above plot shows how to combine a fixed number of plots using ``+``, but what if you want to combine some arbitrarily long list of objects? You can do that by making a ``Layout`` explicitly, which is what ``+`` does internally.\n", "\n", "The following more complicated example shows how complex interactive plots can be generated with relatively little code, and also demonstrates how different HoloViews elements can be combined together. In the following visualization, the black dot denotes a specific longitude, latitude location *(0,10)*, and the curve is a sample of the ``surface_temperature`` at that location. The curve is unaffected by the `time` slider because it already lays out time along the x axis:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%opts Curve [aspect=2 xticks=4 xrotation=15] Points (color='k')\n", "temp_curve = hv.Curve(xr_dataset.select(longitude=0, latitude=10), kdims=['time'])\n", "temp_map = xr_dataset.to(gv.Image,['longitude', 'latitude']) * gv.Points([(0,10)], crs=crs.PlateCarree())\n", "temp_map + temp_curve" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Overlaying data and normalization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's view the surface temperatures together with the global coastline:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%opts Image [projection=crs.Geostationary()] (cmap='Greens') Overlay [xaxis=None yaxis=None]\n", "xr_dataset.to.image(['longitude', 'latitude']) * gf.coastline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that every frame individually uses the full dynamic range of the Greens color map. This is because normalization is set to ``+framewise`` at the top of the notebook, which means every frame is normalized independently. This sort of normalization can be computed on an as-needed basis, using whatever values are found in the current data being shown in a given frame, but it won't let you see how different frames compare to each other.\n", "\n", "To control normalization, we need to decide on the normalization limits. Let's see the maximum temperature in the cube, and use it to set a normalization range by using the redim method:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%opts Image [projection=crs.Geostationary()] (cmap='Greens') Overlay [xaxis=None yaxis=None]\n", "max_surface_temp = xr_dataset.range('surface_temperature')[1]\n", "print(max_surface_temp)\n", "xr_dataset.redim(surface_temperature=dict(range=(300, max_surface_temp))).to(gv.Image,['longitude', 'latitude']) \\\n", " * gf.coastline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By specifying the normalization range we can reveal different aspects of the data. In the example above we can see a cooling effect over time as the dark green areas close to the top of the normalization range (317K) vanish. Values outside this range are clipped to the ends of the color map." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lastly, here is a demo of a conversion from ``surface_temperature`` to ``FilledContours``:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xr_dataset.to(gv.FilledContours,['longitude', 'latitude']) * gf.coastline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, it's quite simple to expose any data you like from xarray, easily and flexibly creating interactive or static visualizations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "gist_info": { "gist_id": null, "gist_url": null }, "hide_input": false, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "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.6.5" }, "widgets": { "state": {}, "version": "1.1.2" } }, "nbformat": 4, "nbformat_minor": 2 }