{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"contentcontainer med left\" style=\"margin-left: -50px;\">\n",
    "<dl class=\"dl-horizontal\">\n",
    "  <dt>Title</dt> <dd> QuadMesh Element</dd>\n",
    "  <dt>Dependencies</dt> <dd>Bokeh</dd>\n",
    "  <dt>Backends</dt> <dd><a href='./QuadMesh.ipynb'>Bokeh</a></dd> <dd><a href='../matplotlib/QuadMesh.ipynb'>Matplotlib</a></dd>\n",
    "</dl>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import holoviews as hv\n",
    "from holoviews import opts\n",
    "hv.extension('bokeh')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A ``QuadMesh`` represents a 2D rectangular grid expressed as x- and y-coordinates defined as 1D or 2D arrays. Unlike an ``Image``, a QuadMesh may be regularly or irregularly spaced and contain either bin edges or bin centers. If bin edges are supplied, the shape of the x/y-coordinate arrays should be one greater than the value array.\n",
    "\n",
    "The default interface expects data to be specified in the form:\n",
    "\n",
    "    QuadMesh((X, Y, Z))\n",
    "\n",
    "where ``X`` and ``Y`` may be 1D or 2D arrays of the shape ``N(+1)`` and ``M(+1)`` respectively or ``N(+1)xM(+1)`` and the ``Z`` value array should be of  shape NxM. Other gridded formats such as xarray are also supported if installed.\n",
    "\n",
    "The grid orientation follows the standard matrix convention: An array ``Z`` with shape (nrows, ncolumns) is plotted with the column number as ``X`` and the row number as ``Y``. See the [Gridded Datasets](../../../user_guide/09-Gridded_Datasets.ipynb) user guide for all the other accepted data formats.\n",
    "\n",
    "Here is a simple ``QuadMesh`` with logarithmic sampling along the 'x' dimensions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 8  # Number of bins in each direction\n",
    "xs = np.logspace(1, 3, n)\n",
    "ys = np.linspace(1, 10, n)\n",
    "zs = np.arange((n-1)**2).reshape(n-1, n-1)\n",
    "print('Shape of x-coordinates:', xs.shape)\n",
    "print('Shape of y-coordinates:', ys.shape)\n",
    "print('Shape of value array:', zs.shape)\n",
    "hv.QuadMesh((xs, ys, zs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The coordinate system of a ``QuadMesh`` is defined by the bin edges, therefore any index falling into a binned region will return the appropriate value. ``QuadMesh`` supports inspection of values using the Bokeh hover tools and because the bin edges have continuous values, you can use non-linear axes such as log axes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "linearscale = hv.QuadMesh((xs, ys, zs)).opts(\n",
    "    opts.QuadMesh(title='LinearScale', tools=['hover'], xticks=[10, 100, 1000]))\n",
    "logscale = hv.QuadMesh((xs, ys, zs)).opts(\n",
    "    opts.QuadMesh(title='LogScale', logx=True))\n",
    "linearscale + logscale"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Unlike ``Image`` objects, slices must be inclusive of the bin edges but otherwise the slicing semantics are the same. The reason for this difference is that ``QuadMesh`` is really a two-dimensional histogram and when slicing, you only want to see the bins that fall within the specified slice ranges.\n",
    "\n",
    "In the next example, we specify a slice along the x- and y-axis to extract the lower corner and we set the z-dimension range to maintain the full color range of the colormap:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "qmesh = hv.QuadMesh((xs, ys, zs))\n",
    "qmesh[20:400, :8].redim.range(z=qmesh.range('z'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For full documentation and the available style and plot options, use ``hv.help(hv.QuadMesh).``"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Irregular meshes\n",
    "\n",
    "In addition to axis aligned meshes like those we worked with above, a ``QuadMesh`` may also be used to represent irregular or unstructured meshes. In this example we will create an irregular mesh consisting of 2D X, Y and Z arrays defining the position and value of each simplex in the mesh:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n=20\n",
    "coords = np.linspace(-1.5,1.5,n)\n",
    "X,Y = np.meshgrid(coords, coords);\n",
    "Qx = np.cos(Y) - np.cos(X)\n",
    "Qy = np.sin(Y) + np.sin(X)\n",
    "Z = np.sqrt(X**2 + Y**2)\n",
    "\n",
    "print('Shape of x-coordinates:', Qx.shape)\n",
    "print('Shape of y-coordinates:', Qy.shape)\n",
    "print('Shape of value array:', Z.shape)\n",
    "\n",
    "qmesh = hv.QuadMesh((Qx, Qy, Z))\n",
    "qmesh"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To illustrate irregular meshes a bit further we will randomly jitter the mesh coordinates along both dimensions, demonstrating that ``QuadMesh`` may be used to represent completely arbitrary meshes. It may also be used to represent overlapping meshes, however the behavior during slicing and other operations may not be well defined in such cases."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(13)\n",
    "xs, ys = np.meshgrid(np.linspace(-20, 20, 10), np.linspace(0, 30, 8))\n",
    "xs += xs/10 + np.random.rand(*xs.shape)*4\n",
    "ys += ys/10 + np.random.rand(*ys.shape)*4\n",
    "\n",
    "zs = np.arange(80).reshape(8, 10)\n",
    "hv.QuadMesh((xs, ys, zs))"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
