{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# NCL_panel_35.py\nThis script illustrates the following concepts:\n   - Attaching three filled contour plots along Y axes\n   - Adding a common colorbar to attached plots\n   - Adding a common title to attached plots\n   - Generating dummy data using \"generate_2d_array\"\n   - Drawing a custom colorbar\n   - Drawing a custom title\n   - Retrieving the bounding box of a plot\n\nSee following URLs to see the reproduced NCL plot & script:\n    - Original NCL script: http://www.ncl.ucar.edu/Applications/Scripts/panel_35.ncl\n    - Original NCL plot: http://www.ncl.ucar.edu/Applications/Images/panel_35_lg.png\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Import packages:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\nimport numpy as np\n\nfrom geocat.viz import cmaps as gvcmaps\nimport geocat.viz.util as gvutil\n\nimport math"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Definition of generate_2d_array and helper functions from https://github.com/NCAR/pyngl/blob/develop/src/ngl/__init__.py\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "#  Globals for random number generator for generat_2d_array\n\ndfran_iseq = 0\ndfran_rseq = [.749, .973, .666, .804, .081, .483, .919, .903,   \\\n              .951, .960, .039, .269, .270, .756, .222, .478,   \\\n              .621, .063, .550, .798, .027, .569, .149, .697,   \\\n              .451, .738, .508, .041, .266, .249, .019, .191,   \\\n              .266, .625, .492, .940, .508, .406, .972, .311,   \\\n              .757, .378, .299, .536, .619, .844, .342, .295,   \\\n              .447, .499, .688, .193, .225, .520, .954, .749,   \\\n              .997, .693, .217, .273, .961, .948, .902, .104,   \\\n              .495, .257, .524, .100, .492, .347, .981, .019,   \\\n              .225, .806, .678, .710, .235, .600, .994, .758,   \\\n              .682, .373, .009, .469, .203, .730, .588, .603,   \\\n              .213, .495, .884, .032, .185, .127, .010, .180,   \\\n              .689, .354, .372, .429                            \\\n             ]\n\n#  Random number generator for generate_2d_array.\n\n\ndef _dfran():\n    global dfran_iseq\n    global dfran_rseq\n    dfran_iseq = dfran_iseq % 100\n    r = dfran_rseq[dfran_iseq]\n    dfran_iseq = dfran_iseq + 1\n    return r\n\ndef generate_2d_array(dims, num_low, num_high, minv, maxv, seed=0, \\\n                      highs_at=None, lows_at=None):\n    \"\"\"\nGenerates smooth 2D arrays primarily for use in examples.\narray = generate_2d_array(dims, num_low, num_high, minv, maxv, seed=0,\n                          highs_at=None, lows_at=None)\ndims -- a list (or array) containing the dimensions of the\n        two-dimensional array to be returned.\nnum_low, num_high -- Integers representing the approximate minimum \n                     and maximum number of highs and lows that the \n                     output array will have. They must be in the \n                     range 1 to 25. If not, then they will be set to \n                     either 1 or 25.\nminv, maxv -- The exact minimum and maximum values that the output array \n              will have.\niseed -- an optional argument specifying a seed for the random number\n         generator.  If iseed is outside the range 0 to 99, it will\n         be set to 0.\nlows_at -- an optional argument that is a list of coordinate  \n           pairs specifying where the lows will occur.  If this\n           argument appears, then its length must equal num_low and\n           the coordinates must be in the ranges specified in dims.\nhighs_at -- an optional argument that is a list of coordinate  \n            pairs specifying where the highs will occur.  If this\n            argument appears, then its length must equal num_high and\n            the coordinates must be in the ranges specified in dims.\n  \"\"\"\n\n    #  Globals for random numbers.\n\n    global dfran_iseq\n    dfran_iseq = seed\n\n    #  Check arguments.\n\n    try:\n        alen = len(dims)\n    except:\n        print(\n            \"generate_2d_array: first argument must be a list, tuple, or array having two elements specifying the dimensions of the output array.\"\n        )\n        return None\n    if (alen != 2):\n        print(\n            \"generate_2d_array: first argument must have two elements specifying the dimensions of the output array.\"\n        )\n        return None\n    if (int(dims[0]) <= 1 and int(dims[1]) <= 1):\n        print(\"generate_2d_array: array must have at least two elements.\")\n        return None\n    if (num_low < 1):\n        print(\n            \"generate_2d_array: number of lows must be at least 1 - defaulting to 1.\"\n        )\n        num_low = 1\n    if (num_low > 25):\n        print(\n            \"generate_2d_array: number of lows must be at most 25 - defaulting to 25.\"\n        )\n        num_high = 25\n    if (num_high < 1):\n        print(\n            \"generate_2d_array: number of highs must be at least 1 - defaulting to 1.\"\n        )\n        num_high = 1\n    if (num_high > 25):\n        print(\n            \"generate_2d_array: number of highs must be at most 25 - defaulting to 25.\"\n        )\n        num_high = 25\n    if (seed > 100 or seed < 0):\n        print(\n            \"generate_2d_array: seed must be in the interval [0,100] - seed set to 0.\"\n        )\n        seed = 0\n    if not lows_at is None:\n        if (len(lows_at) != num_low):\n            print(\n                \"generate_2d_array: the list of positions for the lows must be the same size as num_low.\"\n            )\n    if not highs_at is None:\n        if (len(highs_at) != num_high):\n            print(\n                \"generate_2d_array: the list of positions for the highs must be the same size as num_high.\"\n            )\n\n\n#  Dims are reversed in order to get the same results as the NCL function.\n\n    nx = int(dims[1])\n    ny = int(dims[0])\n    out_array = np.zeros([nx, ny], 'f')\n    tmp_array = np.zeros([3, 51], 'f')\n    fovm = 9. / float(nx)\n    fovn = 9. / float(ny)\n    nlow = max(1, min(25, num_low))\n    nhgh = max(1, min(25, num_high))\n    ncnt = nlow + nhgh\n\n    for k in range(num_low):\n        if not lows_at is None:\n            tmp_array[0,\n                      k] = float(lows_at[k][1])  # lows at specified locations.\n            tmp_array[1, k] = float(lows_at[k][0])\n            tmp_array[2, k] = -1.\n        else:\n            tmp_array[0, k] = 1. + (float(nx) -\n                                    1.) * _dfran()  # lows at random locations.\n            tmp_array[1, k] = 1. + (float(ny) -\n                                    1.) * _dfran()  # lows at random locations.\n            tmp_array[2, k] = -1.\n    for k in range(num_low, num_low + num_high):\n        if not highs_at is None:\n            tmp_array[0, k] = float(highs_at[k - num_low][1])  # highs locations\n            tmp_array[1, k] = float(highs_at[k - num_low][0])  # highs locations\n            tmp_array[2, k] = 1.\n        else:\n            tmp_array[0, k] = 1. + (float(nx) -\n                                    1.) * _dfran()  # highs at random locations.\n            tmp_array[1, k] = 1. + (float(ny) -\n                                    1.) * _dfran()  # highs at random locations.\n            tmp_array[2, k] = 1.\n\n    dmin = 1.e+36\n    dmax = -1.e+36\n    midpt = 0.5 * (minv + maxv)\n    for j in range(ny):\n        for i in range(nx):\n            out_array[i, j] = midpt\n            for k in range(ncnt):\n                tempi = fovm * (float(i + 1) - tmp_array[0, k])\n                tempj = fovn * (float(j + 1) - tmp_array[1, k])\n                temp = -(tempi * tempi + tempj * tempj)\n                if (temp >= -20.):\n                    out_array[i,j] = out_array[i,j] +    \\\n                       0.5*(maxv - minv)*tmp_array[2,k]*math.exp(temp)\n            dmin = min(dmin, out_array[i, j])\n            dmax = max(dmax, out_array[i, j])\n\n    out_array = (((out_array - dmin) / (dmax - dmin)) * (maxv - minv)) + minv\n\n    del tmp_array\n\n    return np.transpose(out_array, [1, 0])\n\n\ndef _get_double(obj, name):\n    return (NhlGetDouble(_int_id(obj), name))\n\n\ndef _get_double_array(obj, name):\n    return (NhlGetDoubleArray(_int_id(obj), name))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Create dummy data\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "nx = 100\nny = 100\ndata1 = generate_2d_array((ny, nx), 10, 10, -19., 16., 0)\ndata2 = generate_2d_array((ny, nx), 10, 10, -28., 15., 1)\ndata3 = generate_2d_array((ny, nx), 10, 10, -25., 18., 2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Create figure and axes using gvutil\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, axs = plt.subplots(1,\n                        3,\n                        figsize=(12, 6),\n                        sharex='all',\n                        sharey='all',\n                        gridspec_kw={'wspace': 0})\n\n# Use geocat.viz.util convenience function to set axes tick values\ngvutil.set_axes_limits_and_ticks(axs[0],\n                                 xticks=np.arange(0, 100, 20),\n                                 yticks=np.arange(0, 100, 20),\n                                 xticklabels=np.arange(0, 100, 20),\n                                 yticklabels=np.arange(0, 100, 20))\n# Use geocat.viz.util convenience function to add minor and major tick lines\ngvutil.add_major_minor_ticks(axs[0], x_minor_per_major=4, y_minor_per_major=4)\n# Specify which edges of the subplot should have tick lines\naxs[0].tick_params(axis='both', which='both', left=True, right=False)\n# Force subplot to be square\naxs[0].set_aspect(aspect='equal')\n\n# Repeat for other subplots with a few changes\ngvutil.set_axes_limits_and_ticks(axs[1],\n                                 xticks=np.arange(0, 100, 20),\n                                 yticks=np.arange(0, 100, 20),\n                                 xticklabels=np.arange(0, 100, 20),\n                                 yticklabels=np.arange(0, 100, 20))\ngvutil.add_major_minor_ticks(axs[1], x_minor_per_major=4, y_minor_per_major=4)\naxs[1].tick_params(axis='both', which='both', left=False, right=False)\naxs[1].set_aspect(aspect='equal')\n\ngvutil.set_axes_limits_and_ticks(axs[2],\n                                 xticks=np.arange(0, 100, 20),\n                                 yticks=np.arange(0, 100, 20),\n                                 xticklabels=np.arange(0, 100, 20),\n                                 yticklabels=np.arange(0, 100, 20))\ngvutil.add_major_minor_ticks(axs[2], x_minor_per_major=4, y_minor_per_major=4)\naxs[2].tick_params(axis='both', which='both', left=False, right=True)\naxs[2].set_aspect(aspect='equal')\n\n# Plot data and create colorbar\nnewcmap = gvcmaps.BlueYellowRed\n# levels=contour_levels ensures that each plot has the same scale\ncontour_levels = np.arange(-32, 24, 4)\n\nfilled1 = axs[0].contourf(data1, cmap=newcmap, levels=contour_levels)\naxs[0].contour(filled1, colors='black', linestyles='solid', linewidths=0.4)\nfilled2 = axs[1].contourf(data2, cmap=newcmap, levels=contour_levels)\naxs[1].contour(filled2, colors='black', linestyles='solid', linewidths=0.4)\nfilled3 = axs[2].contourf(data3, cmap=newcmap, levels=contour_levels)\naxs[2].contour(filled3, colors='black', linestyles='solid', linewidths=0.4)\n\nplt.colorbar(filled3,\n             orientation='horizontal',\n             ax=axs,\n             ticks=np.arange(-28, 20, 4),\n             shrink=0.75,\n             drawedges=True,\n             pad=0.1)\n\n# Add title\nfig.suptitle(\"Three dummy plots attached along Y axes\",\n             horizontalalignment='center',\n             y=0.9,\n             fontsize=18,\n             fontweight='bold',\n             fontfamily='sans-serif')\n\nplt.show()"
      ]
    }
  ],
  "metadata": {
    "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.7.9"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}