From 77eb2d3fdcec5c978c64e025ced2764c57c00285 Mon Sep 17 00:00:00 2001
From: shumkovnd <shumkovnd@yandex-team.com>
Date: Fri, 10 Nov 2023 14:39:34 +0300
Subject: KIKIMR-19287: add task_stats_drawing script

---
 .../py2/mpl_toolkits/axes_grid1/__init__.py        |  12 +
 .../mpl_toolkits/axes_grid1/anchored_artists.py    | 376 ++++++++
 .../py2/mpl_toolkits/axes_grid1/axes_divider.py    | 975 +++++++++++++++++++++
 .../py2/mpl_toolkits/axes_grid1/axes_grid.py       | 771 ++++++++++++++++
 .../py2/mpl_toolkits/axes_grid1/axes_rgb.py        | 228 +++++
 .../py2/mpl_toolkits/axes_grid1/axes_size.py       | 323 +++++++
 .../py2/mpl_toolkits/axes_grid1/colorbar.py        | 836 ++++++++++++++++++
 .../py2/mpl_toolkits/axes_grid1/inset_locator.py   | 659 ++++++++++++++
 .../py2/mpl_toolkits/axes_grid1/mpl_axes.py        | 154 ++++
 .../py2/mpl_toolkits/axes_grid1/parasite_axes.py   | 486 ++++++++++
 10 files changed, 4820 insertions(+)
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/__init__.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/anchored_artists.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_divider.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_grid.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_rgb.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_size.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/colorbar.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/inset_locator.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/mpl_axes.py
 create mode 100644 contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/parasite_axes.py

(limited to 'contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1')

diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/__init__.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/__init__.py
new file mode 100644
index 0000000000..3e225ba9f0
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/__init__.py
@@ -0,0 +1,12 @@
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import six
+
+from . import axes_size as Size
+from .axes_divider import Divider, SubplotDivider, LocatableAxes, \
+     make_axes_locatable
+from .axes_grid import Grid, ImageGrid, AxesGrid
+#from axes_divider import make_axes_locatable
+
+from .parasite_axes import host_subplot, host_axes
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/anchored_artists.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/anchored_artists.py
new file mode 100644
index 0000000000..5b492858e8
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/anchored_artists.py
@@ -0,0 +1,376 @@
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+import six
+
+from matplotlib import docstring
+from matplotlib.offsetbox import (AnchoredOffsetbox, AuxTransformBox,
+                                  DrawingArea, TextArea, VPacker)
+from matplotlib.patches import Rectangle, Ellipse
+
+
+__all__ = ['AnchoredDrawingArea', 'AnchoredAuxTransformBox',
+           'AnchoredEllipse', 'AnchoredSizeBar']
+
+
+class AnchoredDrawingArea(AnchoredOffsetbox):
+    @docstring.dedent
+    def __init__(self, width, height, xdescent, ydescent,
+                 loc, pad=0.4, borderpad=0.5, prop=None, frameon=True,
+                 **kwargs):
+        """
+        An anchored container with a fixed size and fillable DrawingArea.
+
+        Artists added to the *drawing_area* will have their coordinates
+        interpreted as pixels. Any transformations set on the artists will be
+        overridden.
+
+        Parameters
+        ----------
+        width, height : int or float
+            width and height of the container, in pixels.
+
+        xdescent, ydescent : int or float
+            descent of the container in the x- and y- direction, in pixels.
+
+        loc : int
+            Location of this artist. Valid location codes are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4,
+                'right'        : 5,
+                'center left'  : 6,
+                'center right' : 7,
+                'lower center' : 8,
+                'upper center' : 9,
+                'center'       : 10
+
+        pad : int or float, optional
+            Padding around the child objects, in fraction of the font
+            size. Defaults to 0.4.
+
+        borderpad : int or float, optional
+            Border padding, in fraction of the font size.
+            Defaults to 0.5.
+
+        prop : `matplotlib.font_manager.FontProperties`, optional
+            Font property used as a reference for paddings.
+
+        frameon : bool, optional
+            If True, draw a box around this artists. Defaults to True.
+
+        **kwargs :
+            Keyworded arguments to pass to
+            :class:`matplotlib.offsetbox.AnchoredOffsetbox`.
+
+        Attributes
+        ----------
+        drawing_area : `matplotlib.offsetbox.DrawingArea`
+            A container for artists to display.
+
+        Examples
+        --------
+        To display blue and red circles of different sizes in the upper right
+        of an axes *ax*:
+
+        >>> ada = AnchoredDrawingArea(20, 20, 0, 0, loc=1, frameon=False)
+        >>> ada.drawing_area.add_artist(Circle((10, 10), 10, fc="b"))
+        >>> ada.drawing_area.add_artist(Circle((30, 10), 5, fc="r"))
+        >>> ax.add_artist(ada)
+        """
+        self.da = DrawingArea(width, height, xdescent, ydescent)
+        self.drawing_area = self.da
+
+        super(AnchoredDrawingArea, self).__init__(
+            loc, pad=pad, borderpad=borderpad, child=self.da, prop=None,
+            frameon=frameon, **kwargs
+        )
+
+
+class AnchoredAuxTransformBox(AnchoredOffsetbox):
+    @docstring.dedent
+    def __init__(self, transform, loc,
+                 pad=0.4, borderpad=0.5, prop=None, frameon=True, **kwargs):
+        """
+        An anchored container with transformed coordinates.
+
+        Artists added to the *drawing_area* are scaled according to the
+        coordinates of the transformation used. The dimensions of this artist
+        will scale to contain the artists added.
+
+        Parameters
+        ----------
+        transform : `matplotlib.transforms.Transform`
+            The transformation object for the coordinate system in use, i.e.,
+            :attr:`matplotlib.axes.Axes.transData`.
+
+        loc : int
+            Location of this artist. Valid location codes are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4,
+                'right'        : 5,
+                'center left'  : 6,
+                'center right' : 7,
+                'lower center' : 8,
+                'upper center' : 9,
+                'center'       : 10
+
+        pad : int or float, optional
+            Padding around the child objects, in fraction of the font
+            size. Defaults to 0.4.
+
+        borderpad : int or float, optional
+            Border padding, in fraction of the font size.
+            Defaults to 0.5.
+
+        prop : `matplotlib.font_manager.FontProperties`, optional
+            Font property used as a reference for paddings.
+
+        frameon : bool, optional
+            If True, draw a box around this artists. Defaults to True.
+
+        **kwargs :
+            Keyworded arguments to pass to
+            :class:`matplotlib.offsetbox.AnchoredOffsetbox`.
+
+        Attributes
+        ----------
+        drawing_area : `matplotlib.offsetbox.AuxTransformBox`
+            A container for artists to display.
+
+        Examples
+        --------
+        To display an ellipse in the upper left, with a width of 0.1 and
+        height of 0.4 in data coordinates:
+
+        >>> box = AnchoredAuxTransformBox(ax.transData, loc=2)
+        >>> el = Ellipse((0,0), width=0.1, height=0.4, angle=30)
+        >>> box.drawing_area.add_artist(el)
+        >>> ax.add_artist(box)
+        """
+        self.drawing_area = AuxTransformBox(transform)
+
+        AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
+                                   child=self.drawing_area,
+                                   prop=prop,
+                                   frameon=frameon,
+                                   **kwargs)
+
+
+class AnchoredEllipse(AnchoredOffsetbox):
+    @docstring.dedent
+    def __init__(self, transform, width, height, angle, loc,
+                 pad=0.1, borderpad=0.1, prop=None, frameon=True, **kwargs):
+        """
+        Draw an anchored ellipse of a given size.
+
+        Parameters
+        ----------
+        transform : `matplotlib.transforms.Transform`
+            The transformation object for the coordinate system in use, i.e.,
+            :attr:`matplotlib.axes.Axes.transData`.
+
+        width, height : int or float
+            Width and height of the ellipse, given in coordinates of
+            *transform*.
+
+        angle : int or float
+            Rotation of the ellipse, in degrees, anti-clockwise.
+
+        loc : int
+            Location of this size bar. Valid location codes are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4,
+                'right'        : 5,
+                'center left'  : 6,
+                'center right' : 7,
+                'lower center' : 8,
+                'upper center' : 9,
+                'center'       : 10
+
+        pad : int or float, optional
+            Padding around the ellipse, in fraction of the font size. Defaults
+            to 0.1.
+
+        borderpad : int or float, optional
+            Border padding, in fraction of the font size. Defaults to 0.1.
+
+        frameon : bool, optional
+            If True, draw a box around the ellipse. Defaults to True.
+
+        prop : `matplotlib.font_manager.FontProperties`, optional
+            Font property used as a reference for paddings.
+
+        **kwargs :
+            Keyworded arguments to pass to
+            :class:`matplotlib.offsetbox.AnchoredOffsetbox`.
+
+        Attributes
+        ----------
+        ellipse : `matplotlib.patches.Ellipse`
+            Ellipse patch drawn.
+        """
+        self._box = AuxTransformBox(transform)
+        self.ellipse = Ellipse((0, 0), width, height, angle)
+        self._box.add_artist(self.ellipse)
+
+        AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
+                                   child=self._box,
+                                   prop=prop,
+                                   frameon=frameon, **kwargs)
+
+
+class AnchoredSizeBar(AnchoredOffsetbox):
+    @docstring.dedent
+    def __init__(self, transform, size, label, loc,
+                 pad=0.1, borderpad=0.1, sep=2,
+                 frameon=True, size_vertical=0, color='black',
+                 label_top=False, fontproperties=None, fill_bar=None,
+                 **kwargs):
+        """
+        Draw a horizontal scale bar with a center-aligned label underneath.
+
+        Parameters
+        ----------
+        transform : `matplotlib.transforms.Transform`
+            The transformation object for the coordinate system in use, i.e.,
+            :attr:`matplotlib.axes.Axes.transData`.
+
+        size : int or float
+            Horizontal length of the size bar, given in coordinates of
+            *transform*.
+
+        label : str
+            Label to display.
+
+        loc : int
+            Location of this size bar. Valid location codes are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4,
+                'right'        : 5,
+                'center left'  : 6,
+                'center right' : 7,
+                'lower center' : 8,
+                'upper center' : 9,
+                'center'       : 10
+
+        pad : int or float, optional
+            Padding around the label and size bar, in fraction of the font
+            size. Defaults to 0.1.
+
+        borderpad : int or float, optional
+            Border padding, in fraction of the font size.
+            Defaults to 0.1.
+
+        sep : int or float, optional
+            Separation between the label and the size bar, in points.
+            Defaults to 2.
+
+        frameon : bool, optional
+            If True, draw a box around the horizontal bar and label.
+            Defaults to True.
+
+        size_vertical : int or float, optional
+            Vertical length of the size bar, given in coordinates of
+            *transform*. Defaults to 0.
+
+        color : str, optional
+            Color for the size bar and label.
+            Defaults to black.
+
+        label_top : bool, optional
+            If True, the label will be over the size bar.
+            Defaults to False.
+
+        fontproperties : `matplotlib.font_manager.FontProperties`, optional
+            Font properties for the label text.
+
+        fill_bar : bool, optional
+            If True and if size_vertical is nonzero, the size bar will
+            be filled in with the color specified by the size bar.
+            Defaults to True if `size_vertical` is greater than
+            zero and False otherwise.
+
+        **kwargs :
+            Keyworded arguments to pass to
+            :class:`matplotlib.offsetbox.AnchoredOffsetbox`.
+
+        Attributes
+        ----------
+        size_bar : `matplotlib.offsetbox.AuxTransformBox`
+            Container for the size bar.
+
+        txt_label : `matplotlib.offsetbox.TextArea`
+            Container for the label of the size bar.
+
+        Notes
+        -----
+        If *prop* is passed as a keyworded argument, but *fontproperties* is
+        not, then *prop* is be assumed to be the intended *fontproperties*.
+        Using both *prop* and *fontproperties* is not supported.
+
+        Examples
+        --------
+        >>> import matplotlib.pyplot as plt
+        >>> import numpy as np
+        >>> from mpl_toolkits.axes_grid1.anchored_artists import \
+AnchoredSizeBar
+        >>> fig, ax = plt.subplots()
+        >>> ax.imshow(np.random.random((10,10)))
+        >>> bar = AnchoredSizeBar(ax.transData, 3, '3 data units', 4)
+        >>> ax.add_artist(bar)
+        >>> fig.show()
+
+        Using all the optional parameters
+
+        >>> import matplotlib.font_manager as fm
+        >>> fontprops = fm.FontProperties(size=14, family='monospace')
+        >>> bar = AnchoredSizeBar(ax.transData, 3, '3 units', 4, pad=0.5, \
+sep=5, borderpad=0.5, frameon=False, \
+size_vertical=0.5, color='white', \
+fontproperties=fontprops)
+        """
+        if fill_bar is None:
+            fill_bar = size_vertical > 0
+
+        self.size_bar = AuxTransformBox(transform)
+        self.size_bar.add_artist(Rectangle((0, 0), size, size_vertical,
+                                           fill=fill_bar, facecolor=color,
+                                           edgecolor=color))
+
+        if fontproperties is None and 'prop' in kwargs:
+            fontproperties = kwargs.pop('prop')
+
+        if fontproperties is None:
+            textprops = {'color': color}
+        else:
+            textprops = {'color': color, 'fontproperties': fontproperties}
+
+        self.txt_label = TextArea(
+            label,
+            minimumdescent=False,
+            textprops=textprops)
+
+        if label_top:
+            _box_children = [self.txt_label, self.size_bar]
+        else:
+            _box_children = [self.size_bar, self.txt_label]
+
+        self._box = VPacker(children=_box_children,
+                            align="center",
+                            pad=0, sep=sep)
+
+        AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
+                                   child=self._box,
+                                   prop=fontproperties,
+                                   frameon=frameon, **kwargs)
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_divider.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_divider.py
new file mode 100644
index 0000000000..b238e73cc5
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_divider.py
@@ -0,0 +1,975 @@
+"""
+The axes_divider module provides helper classes to adjust the positions of
+multiple axes at drawing time.
+
+ Divider: this is the class that is used to calculate the axes
+    position. It divides the given rectangular area into several sub
+    rectangles. You initialize the divider by setting the horizontal
+    and vertical lists of sizes that the division will be based on. You
+    then use the new_locator method, whose return value is a callable
+    object that can be used to set the axes_locator of the axes.
+
+"""
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import six
+from six.moves import map
+
+import matplotlib.transforms as mtransforms
+
+from matplotlib.axes import SubplotBase
+
+from . import axes_size as Size
+
+
+class Divider(object):
+    """
+    This class calculates the axes position. It
+    divides the given rectangular area into several
+    sub-rectangles. You initialize the divider by setting the
+    horizontal and vertical lists of sizes
+    (:mod:`mpl_toolkits.axes_grid.axes_size`) that the division will
+    be based on. You then use the new_locator method to create a
+    callable object that can be used as the axes_locator of the
+    axes.
+    """
+
+    def __init__(self, fig, pos, horizontal, vertical,
+                 aspect=None, anchor="C"):
+        """
+        Parameters
+        ----------
+        fig : Figure
+        pos : tuple of 4 floats
+            position of the rectangle that will be divided
+        horizontal : list of :mod:`~mpl_toolkits.axes_grid.axes_size`
+            sizes for horizontal division
+        vertical : list of :mod:`~mpl_toolkits.axes_grid.axes_size`
+            sizes for vertical division
+        aspect : bool
+            if True, the overall rectangular area is reduced
+            so that the relative part of the horizontal and
+            vertical scales have the same scale.
+        anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'}
+            placement of the reduced rectangle when *aspect* is True
+        """
+
+        self._fig = fig
+        self._pos = pos
+        self._horizontal = horizontal
+        self._vertical = vertical
+        self._anchor = anchor
+        self._aspect = aspect
+        self._xrefindex = 0
+        self._yrefindex = 0
+        self._locator = None
+
+    def get_horizontal_sizes(self, renderer):
+        return [s.get_size(renderer) for s in self.get_horizontal()]
+
+    def get_vertical_sizes(self, renderer):
+        return [s.get_size(renderer) for s in self.get_vertical()]
+
+    def get_vsize_hsize(self):
+
+        from .axes_size import AddList
+
+        vsize = AddList(self.get_vertical())
+        hsize = AddList(self.get_horizontal())
+
+        return vsize, hsize
+
+    @staticmethod
+    def _calc_k(l, total_size):
+
+        rs_sum, as_sum = 0., 0.
+
+        for _rs, _as in l:
+            rs_sum += _rs
+            as_sum += _as
+
+        if rs_sum != 0.:
+            k = (total_size - as_sum) / rs_sum
+            return k
+        else:
+            return 0.
+
+    @staticmethod
+    def _calc_offsets(l, k):
+
+        offsets = [0.]
+
+        #for s in l:
+        for _rs, _as in l:
+            #_rs, _as = s.get_size(renderer)
+            offsets.append(offsets[-1] + _rs*k + _as)
+
+        return offsets
+
+    def set_position(self, pos):
+        """
+        set the position of the rectangle.
+
+        Parameters
+        ----------
+        pos : tuple of 4 floats
+            position of the rectangle that will be divided
+        """
+        self._pos = pos
+
+    def get_position(self):
+        "return the position of the rectangle."
+        return self._pos
+
+    def set_anchor(self, anchor):
+        """
+        Parameters
+        ----------
+        anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'}
+            anchor position
+
+          =====  ============
+          value  description
+          =====  ============
+          'C'    Center
+          'SW'   bottom left
+          'S'    bottom
+          'SE'   bottom right
+          'E'    right
+          'NE'   top right
+          'N'    top
+          'NW'   top left
+          'W'    left
+          =====  ============
+
+        """
+        if anchor in mtransforms.Bbox.coefs or len(anchor) == 2:
+            self._anchor = anchor
+        else:
+            raise ValueError('argument must be among %s' %
+                             ', '.join(mtransforms.BBox.coefs))
+
+    def get_anchor(self):
+        "return the anchor"
+        return self._anchor
+
+    def set_horizontal(self, h):
+        """
+        Parameters
+        ----------
+        h : list of :mod:`~mpl_toolkits.axes_grid.axes_size`
+            sizes for horizontal division
+        """
+        self._horizontal = h
+
+    def get_horizontal(self):
+        "return horizontal sizes"
+        return self._horizontal
+
+    def set_vertical(self, v):
+        """
+        Parameters
+        ----------
+        v : list of :mod:`~mpl_toolkits.axes_grid.axes_size`
+            sizes for vertical division
+        """
+        self._vertical = v
+
+    def get_vertical(self):
+        "return vertical sizes"
+        return self._vertical
+
+    def set_aspect(self, aspect=False):
+        """
+        Parameters
+        ----------
+        aspect : bool
+        """
+        self._aspect = aspect
+
+    def get_aspect(self):
+        "return aspect"
+        return self._aspect
+
+    def set_locator(self, _locator):
+        self._locator = _locator
+
+    def get_locator(self):
+        return self._locator
+
+    def get_position_runtime(self, ax, renderer):
+        if self._locator is None:
+            return self.get_position()
+        else:
+            return self._locator(ax, renderer).bounds
+
+    def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
+        """
+        Parameters
+        ----------
+        nx, nx1 : int
+            Integers specifying the column-position of the
+            cell. When *nx1* is None, a single *nx*-th column is
+            specified. Otherwise location of columns spanning between *nx*
+            to *nx1* (but excluding *nx1*-th column) is specified.
+        ny, ny1 : int
+            Same as *nx* and *nx1*, but for row positions.
+        axes
+        renderer
+        """
+
+        figW, figH = self._fig.get_size_inches()
+        x, y, w, h = self.get_position_runtime(axes, renderer)
+
+        hsizes = self.get_horizontal_sizes(renderer)
+        vsizes = self.get_vertical_sizes(renderer)
+        k_h = self._calc_k(hsizes, figW*w)
+        k_v = self._calc_k(vsizes, figH*h)
+
+        if self.get_aspect():
+            k = min(k_h, k_v)
+            ox = self._calc_offsets(hsizes, k)
+            oy = self._calc_offsets(vsizes, k)
+
+            ww = (ox[-1] - ox[0])/figW
+            hh = (oy[-1] - oy[0])/figH
+            pb = mtransforms.Bbox.from_bounds(x, y, w, h)
+            pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh)
+            pb1_anchored = pb1.anchored(self.get_anchor(), pb)
+            x0, y0 = pb1_anchored.x0, pb1_anchored.y0
+
+        else:
+            ox = self._calc_offsets(hsizes, k_h)
+            oy = self._calc_offsets(vsizes, k_v)
+            x0, y0 = x, y
+
+        if nx1 is None:
+            nx1 = nx+1
+        if ny1 is None:
+            ny1 = ny+1
+
+        x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW
+        y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH
+
+        return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
+
+    def new_locator(self, nx, ny, nx1=None, ny1=None):
+        """
+        Returns a new locator
+        (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for
+        specified cell.
+
+        Parameters
+        ----------
+        nx, nx1 : int
+            Integers specifying the column-position of the
+            cell. When *nx1* is None, a single *nx*-th column is
+            specified. Otherwise location of columns spanning between *nx*
+            to *nx1* (but excluding *nx1*-th column) is specified.
+        ny, ny1 : int
+            Same as *nx* and *nx1*, but for row positions.
+        """
+        return AxesLocator(self, nx, ny, nx1, ny1)
+
+    def append_size(self, position, size):
+
+        if position == "left":
+            self._horizontal.insert(0, size)
+            self._xrefindex += 1
+        elif position == "right":
+            self._horizontal.append(size)
+        elif position == "bottom":
+            self._vertical.insert(0, size)
+            self._yrefindex += 1
+        elif position == "top":
+            self._vertical.append(size)
+        else:
+            raise ValueError("the position must be one of left," +
+                             " right, bottom, or top")
+
+    def add_auto_adjustable_area(self,
+                                 use_axes, pad=0.1,
+                                 adjust_dirs=None,
+                                 ):
+        if adjust_dirs is None:
+            adjust_dirs = ["left", "right", "bottom", "top"]
+        from .axes_size import Padded, SizeFromFunc, GetExtentHelper
+        for d in adjust_dirs:
+            helper = GetExtentHelper(use_axes, d)
+            size = SizeFromFunc(helper)
+            padded_size = Padded(size, pad)  # pad in inch
+            self.append_size(d, padded_size)
+
+
+class AxesLocator(object):
+    """
+    A simple callable object, initialized with AxesDivider class,
+    returns the position and size of the given cell.
+    """
+    def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None):
+        """
+        Parameters
+        ----------
+        axes_divider : AxesDivider
+        nx, nx1 : int
+            Integers specifying the column-position of the
+            cell. When *nx1* is None, a single *nx*-th column is
+            specified. Otherwise location of columns spanning between *nx*
+            to *nx1* (but excluding *nx1*-th column) is specified.
+        ny, ny1 : int
+            Same as *nx* and *nx1*, but for row positions.
+        """
+        self._axes_divider = axes_divider
+
+        _xrefindex = axes_divider._xrefindex
+        _yrefindex = axes_divider._yrefindex
+
+        self._nx, self._ny = nx - _xrefindex, ny - _yrefindex
+
+        if nx1 is None:
+            nx1 = nx+1
+        if ny1 is None:
+            ny1 = ny+1
+
+        self._nx1 = nx1 - _xrefindex
+        self._ny1 = ny1 - _yrefindex
+
+    def __call__(self, axes, renderer):
+
+        _xrefindex = self._axes_divider._xrefindex
+        _yrefindex = self._axes_divider._yrefindex
+
+        return self._axes_divider.locate(self._nx + _xrefindex,
+                                         self._ny + _yrefindex,
+                                         self._nx1 + _xrefindex,
+                                         self._ny1 + _yrefindex,
+                                         axes,
+                                         renderer)
+
+    def get_subplotspec(self):
+        if hasattr(self._axes_divider, "get_subplotspec"):
+            return self._axes_divider.get_subplotspec()
+        else:
+            return None
+
+
+from matplotlib.gridspec import SubplotSpec, GridSpec
+
+
+class SubplotDivider(Divider):
+    """
+    The Divider class whose rectangle area is specified as a subplot geometry.
+    """
+
+    def __init__(self, fig, *args, **kwargs):
+        """
+        Parameters
+        ----------
+        fig : :class:`matplotlib.figure.Figure`
+        args : tuple (*numRows*, *numCols*, *plotNum*)
+            The array of subplots in the figure has dimensions *numRows*,
+            *numCols*, and *plotNum* is the number of the subplot
+            being created.  *plotNum* starts at 1 in the upper left
+            corner and increases to the right.
+
+            If *numRows* <= *numCols* <= *plotNum* < 10, *args* can be the
+            decimal integer *numRows* * 100 + *numCols* * 10 + *plotNum*.
+        """
+
+        self.figure = fig
+
+        if len(args) == 1:
+            if isinstance(args[0], SubplotSpec):
+                self._subplotspec = args[0]
+            else:
+                try:
+                    s = str(int(args[0]))
+                    rows, cols, num = map(int, s)
+                except ValueError:
+                    raise ValueError(
+                        'Single argument to subplot must be a 3-digit integer')
+                self._subplotspec = GridSpec(rows, cols)[num-1]
+                # num - 1 for converting from MATLAB to python indexing
+        elif len(args) == 3:
+            rows, cols, num = args
+            rows = int(rows)
+            cols = int(cols)
+            if isinstance(num, tuple) and len(num) == 2:
+                num = [int(n) for n in num]
+                self._subplotspec = GridSpec(rows, cols)[num[0]-1:num[1]]
+            else:
+                self._subplotspec = GridSpec(rows, cols)[int(num)-1]
+                # num - 1 for converting from MATLAB to python indexing
+        else:
+            raise ValueError('Illegal argument(s) to subplot: %s' % (args,))
+
+        # total = rows*cols
+        # num -= 1    # convert from matlab to python indexing
+        #             # i.e., num in range(0,total)
+        # if num >= total:
+        #     raise ValueError( 'Subplot number exceeds total subplots')
+        # self._rows = rows
+        # self._cols = cols
+        # self._num = num
+
+        # self.update_params()
+
+        # sets self.fixbox
+        self.update_params()
+
+        pos = self.figbox.bounds
+
+        horizontal = kwargs.pop("horizontal", [])
+        vertical = kwargs.pop("vertical", [])
+        aspect = kwargs.pop("aspect", None)
+        anchor = kwargs.pop("anchor", "C")
+
+        if kwargs:
+            raise Exception("")
+
+        Divider.__init__(self, fig, pos, horizontal, vertical,
+                         aspect=aspect, anchor=anchor)
+
+    def get_position(self):
+        "return the bounds of the subplot box"
+
+        self.update_params()  # update self.figbox
+        return self.figbox.bounds
+
+    # def update_params(self):
+    #     'update the subplot position from fig.subplotpars'
+
+    #     rows = self._rows
+    #     cols = self._cols
+    #     num = self._num
+
+    #     pars = self.figure.subplotpars
+    #     left = pars.left
+    #     right = pars.right
+    #     bottom = pars.bottom
+    #     top = pars.top
+    #     wspace = pars.wspace
+    #     hspace = pars.hspace
+    #     totWidth = right-left
+    #     totHeight = top-bottom
+
+    #     figH = totHeight/(rows + hspace*(rows-1))
+    #     sepH = hspace*figH
+
+    #     figW = totWidth/(cols + wspace*(cols-1))
+    #     sepW = wspace*figW
+
+    #     rowNum, colNum =  divmod(num, cols)
+
+    #     figBottom = top - (rowNum+1)*figH - rowNum*sepH
+    #     figLeft = left + colNum*(figW + sepW)
+
+    #     self.figbox = mtransforms.Bbox.from_bounds(figLeft, figBottom,
+    #                                                figW, figH)
+
+    def update_params(self):
+        'update the subplot position from fig.subplotpars'
+
+        self.figbox = self.get_subplotspec().get_position(self.figure)
+
+    def get_geometry(self):
+        'get the subplot geometry, e.g., 2,2,3'
+        rows, cols, num1, num2 = self.get_subplotspec().get_geometry()
+        return rows, cols, num1+1  # for compatibility
+
+    # COVERAGE NOTE: Never used internally or from examples
+    def change_geometry(self, numrows, numcols, num):
+        'change subplot geometry, e.g., from 1,1,1 to 2,2,3'
+        self._subplotspec = GridSpec(numrows, numcols)[num-1]
+        self.update_params()
+        self.set_position(self.figbox)
+
+    def get_subplotspec(self):
+        'get the SubplotSpec instance'
+        return self._subplotspec
+
+    def set_subplotspec(self, subplotspec):
+        'set the SubplotSpec instance'
+        self._subplotspec = subplotspec
+
+
+class AxesDivider(Divider):
+    """
+    Divider based on the pre-existing axes.
+    """
+
+    def __init__(self, axes, xref=None, yref=None):
+        """
+        Parameters
+        ----------
+        axes : :class:`~matplotlib.axes.Axes`
+        xref
+        yref
+        """
+        self._axes = axes
+        if xref is None:
+            self._xref = Size.AxesX(axes)
+        else:
+            self._xref = xref
+        if yref is None:
+            self._yref = Size.AxesY(axes)
+        else:
+            self._yref = yref
+
+        Divider.__init__(self, fig=axes.get_figure(), pos=None,
+                         horizontal=[self._xref], vertical=[self._yref],
+                         aspect=None, anchor="C")
+
+    def _get_new_axes(self, **kwargs):
+        axes = self._axes
+
+        axes_class = kwargs.pop("axes_class", None)
+
+        if axes_class is None:
+            if isinstance(axes, SubplotBase):
+                axes_class = axes._axes_class
+            else:
+                axes_class = type(axes)
+
+        ax = axes_class(axes.get_figure(),
+                        axes.get_position(original=True), **kwargs)
+
+        return ax
+
+    def new_horizontal(self, size, pad=None, pack_start=False, **kwargs):
+        """
+        Add a new axes on the right (or left) side of the main axes.
+
+        Parameters
+        ----------
+        size : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string
+            A width of the axes. If float or string is given, *from_any*
+            function is used to create the size, with *ref_size* set to AxesX
+            instance of the current axes.
+        pad : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string
+            Pad between the axes. It takes same argument as *size*.
+        pack_start : bool
+            If False, the new axes is appended at the end
+            of the list, i.e., it became the right-most axes. If True, it is
+            inserted at the start of the list, and becomes the left-most axes.
+        kwargs
+            All extra keywords arguments are passed to the created axes.
+            If *axes_class* is given, the new axes will be created as an
+            instance of the given class. Otherwise, the same class of the
+            main axes will be used.
+        """
+
+        if pad:
+            if not isinstance(pad, Size._Base):
+                pad = Size.from_any(pad,
+                                    fraction_ref=self._xref)
+            if pack_start:
+                self._horizontal.insert(0, pad)
+                self._xrefindex += 1
+            else:
+                self._horizontal.append(pad)
+
+        if not isinstance(size, Size._Base):
+            size = Size.from_any(size,
+                                 fraction_ref=self._xref)
+
+        if pack_start:
+            self._horizontal.insert(0, size)
+            self._xrefindex += 1
+            locator = self.new_locator(nx=0, ny=self._yrefindex)
+        else:
+            self._horizontal.append(size)
+            locator = self.new_locator(nx=len(self._horizontal)-1, ny=self._yrefindex)
+
+        ax = self._get_new_axes(**kwargs)
+        ax.set_axes_locator(locator)
+
+        return ax
+
+    def new_vertical(self, size, pad=None, pack_start=False, **kwargs):
+        """
+        Add a new axes on the top (or bottom) side of the main axes.
+
+        Parameters
+        ----------
+        size : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string
+            A height of the axes. If float or string is given, *from_any*
+            function is used to create the size, with *ref_size* set to AxesX
+            instance of the current axes.
+        pad : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string
+            Pad between the axes. It takes same argument as *size*.
+        pack_start : bool
+            If False, the new axes is appended at the end
+            of the list, i.e., it became the right-most axes. If True, it is
+            inserted at the start of the list, and becomes the left-most axes.
+        kwargs
+            All extra keywords arguments are passed to the created axes.
+            If *axes_class* is given, the new axes will be created as an
+            instance of the given class. Otherwise, the same class of the
+            main axes will be used.
+        """
+
+        if pad:
+            if not isinstance(pad, Size._Base):
+                pad = Size.from_any(pad,
+                                    fraction_ref=self._yref)
+            if pack_start:
+                self._vertical.insert(0, pad)
+                self._yrefindex += 1
+            else:
+                self._vertical.append(pad)
+
+        if not isinstance(size, Size._Base):
+            size = Size.from_any(size,
+                                 fraction_ref=self._yref)
+
+        if pack_start:
+            self._vertical.insert(0, size)
+            self._yrefindex += 1
+            locator = self.new_locator(nx=self._xrefindex, ny=0)
+        else:
+            self._vertical.append(size)
+            locator = self.new_locator(nx=self._xrefindex, ny=len(self._vertical)-1)
+
+        ax = self._get_new_axes(**kwargs)
+        ax.set_axes_locator(locator)
+
+        return ax
+
+    def append_axes(self, position, size, pad=None, add_to_figure=True,
+                    **kwargs):
+        """
+        create an axes at the given *position* with the same height
+        (or width) of the main axes.
+
+         *position*
+           ["left"|"right"|"bottom"|"top"]
+
+         *size* and *pad* should be axes_grid.axes_size compatible.
+        """
+
+        if position == "left":
+            ax = self.new_horizontal(size, pad, pack_start=True, **kwargs)
+        elif position == "right":
+            ax = self.new_horizontal(size, pad, pack_start=False, **kwargs)
+        elif position == "bottom":
+            ax = self.new_vertical(size, pad, pack_start=True, **kwargs)
+        elif position == "top":
+            ax = self.new_vertical(size, pad, pack_start=False, **kwargs)
+        else:
+            raise ValueError("the position must be one of left," +
+                             " right, bottom, or top")
+
+        if add_to_figure:
+            self._fig.add_axes(ax)
+        return ax
+
+    def get_aspect(self):
+        if self._aspect is None:
+            aspect = self._axes.get_aspect()
+            if aspect == "auto":
+                return False
+            else:
+                return True
+        else:
+            return self._aspect
+
+    def get_position(self):
+        if self._pos is None:
+            bbox = self._axes.get_position(original=True)
+            return bbox.bounds
+        else:
+            return self._pos
+
+    def get_anchor(self):
+        if self._anchor is None:
+            return self._axes.get_anchor()
+        else:
+            return self._anchor
+
+    def get_subplotspec(self):
+        if hasattr(self._axes, "get_subplotspec"):
+            return self._axes.get_subplotspec()
+        else:
+            return None
+
+
+class HBoxDivider(SubplotDivider):
+
+    def __init__(self, fig, *args, **kwargs):
+        SubplotDivider.__init__(self, fig, *args, **kwargs)
+
+    @staticmethod
+    def _determine_karray(equivalent_sizes, appended_sizes,
+                          max_equivalent_size,
+                          total_appended_size):
+
+        n = len(equivalent_sizes)
+        import numpy as np
+        A = np.mat(np.zeros((n+1, n+1), dtype="d"))
+        B = np.zeros((n+1), dtype="d")
+        # AxK = B
+
+        # populated A
+        for i, (r, a) in enumerate(equivalent_sizes):
+            A[i, i] = r
+            A[i, -1] = -1
+            B[i] = -a
+        A[-1, :-1] = [r for r, a in appended_sizes]
+        B[-1] = total_appended_size - sum([a for rs, a in appended_sizes])
+
+        karray_H = (A.I*np.mat(B).T).A1
+        karray = karray_H[:-1]
+        H = karray_H[-1]
+
+        if H > max_equivalent_size:
+            karray = ((max_equivalent_size -
+                      np.array([a for r, a in equivalent_sizes]))
+                      / np.array([r for r, a in equivalent_sizes]))
+        return karray
+
+    @staticmethod
+    def _calc_offsets(appended_sizes, karray):
+        offsets = [0.]
+
+        #for s in l:
+        for (r, a), k in zip(appended_sizes, karray):
+            offsets.append(offsets[-1] + r*k + a)
+
+        return offsets
+
+    def new_locator(self, nx, nx1=None):
+        """
+        returns a new locator
+        (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for
+        specified cell.
+
+        Parameters
+        ----------
+        nx, nx1 : int
+            Integers specifying the column-position of the
+            cell. When *nx1* is None, a single *nx*-th column is
+            specified. Otherwise location of columns spanning between *nx*
+            to *nx1* (but excluding *nx1*-th column) is specified.
+        ny, ny1 : int
+            Same as *nx* and *nx1*, but for row positions.
+        """
+        return AxesLocator(self, nx, 0, nx1, None)
+
+    def _locate(self, x, y, w, h,
+                y_equivalent_sizes, x_appended_sizes,
+                figW, figH):
+        """
+        Parameters
+        ----------
+        x
+        y
+        w
+        h
+        y_equivalent_sizes
+        x_appended_sizes
+        figW
+        figH
+        """
+
+        equivalent_sizes = y_equivalent_sizes
+        appended_sizes = x_appended_sizes
+
+        max_equivalent_size = figH*h
+        total_appended_size = figW*w
+        karray = self._determine_karray(equivalent_sizes, appended_sizes,
+                                        max_equivalent_size,
+                                        total_appended_size)
+
+        ox = self._calc_offsets(appended_sizes, karray)
+
+        ww = (ox[-1] - ox[0])/figW
+        ref_h = equivalent_sizes[0]
+        hh = (karray[0]*ref_h[0] + ref_h[1])/figH
+        pb = mtransforms.Bbox.from_bounds(x, y, w, h)
+        pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh)
+        pb1_anchored = pb1.anchored(self.get_anchor(), pb)
+        x0, y0 = pb1_anchored.x0, pb1_anchored.y0
+
+        return x0, y0, ox, hh
+
+    def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
+        """
+        Parameters
+        ----------
+        axes_divider : AxesDivider
+        nx, nx1 : int
+            Integers specifying the column-position of the
+            cell. When *nx1* is None, a single *nx*-th column is
+            specified. Otherwise location of columns spanning between *nx*
+            to *nx1* (but excluding *nx1*-th column) is specified.
+        ny, ny1 : int
+            Same as *nx* and *nx1*, but for row positions.
+        axes
+        renderer
+        """
+
+        figW, figH = self._fig.get_size_inches()
+        x, y, w, h = self.get_position_runtime(axes, renderer)
+
+        y_equivalent_sizes = self.get_vertical_sizes(renderer)
+        x_appended_sizes = self.get_horizontal_sizes(renderer)
+        x0, y0, ox, hh = self._locate(x, y, w, h,
+                                      y_equivalent_sizes, x_appended_sizes,
+                                      figW, figH)
+        if nx1 is None:
+            nx1 = nx+1
+
+        x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW
+        y1, h1 = y0, hh
+
+        return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
+
+
+class VBoxDivider(HBoxDivider):
+    """
+    The Divider class whose rectangle area is specified as a subplot geometry.
+    """
+
+    def new_locator(self, ny, ny1=None):
+        """
+        returns a new locator
+        (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for
+        specified cell.
+
+        Parameters
+        ----------
+        ny, ny1 : int
+            Integers specifying the row-position of the
+            cell. When *ny1* is None, a single *ny*-th row is
+            specified. Otherwise location of rows spanning between *ny*
+            to *ny1* (but excluding *ny1*-th row) is specified.
+        """
+        return AxesLocator(self, 0, ny, None, ny1)
+
+    def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
+        """
+        Parameters
+        ----------
+        axes_divider : AxesDivider
+        nx, nx1 : int
+            Integers specifying the column-position of the
+            cell. When *nx1* is None, a single *nx*-th column is
+            specified. Otherwise location of columns spanning between *nx*
+            to *nx1* (but excluding *nx1*-th column) is specified.
+        ny, ny1 : int
+            Same as *nx* and *nx1*, but for row positions.
+        axes
+        renderer
+        """
+
+        figW, figH = self._fig.get_size_inches()
+        x, y, w, h = self.get_position_runtime(axes, renderer)
+
+        x_equivalent_sizes = self.get_horizontal_sizes(renderer)
+        y_appended_sizes = self.get_vertical_sizes(renderer)
+
+        y0, x0, oy, ww = self._locate(y, x, h, w,
+                                      x_equivalent_sizes, y_appended_sizes,
+                                      figH, figW)
+        if ny1 is None:
+            ny1 = ny+1
+
+        x1, w1 = x0, ww
+        y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH
+
+        return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
+
+
+class LocatableAxesBase(object):
+    def __init__(self, *kl, **kw):
+
+        self._axes_class.__init__(self, *kl, **kw)
+
+        self._locator = None
+        self._locator_renderer = None
+
+    def set_axes_locator(self, locator):
+        self._locator = locator
+
+    def get_axes_locator(self):
+        return self._locator
+
+    def apply_aspect(self, position=None):
+
+        if self.get_axes_locator() is None:
+            self._axes_class.apply_aspect(self, position)
+        else:
+            pos = self.get_axes_locator()(self, self._locator_renderer)
+            self._axes_class.apply_aspect(self, position=pos)
+
+    def draw(self, renderer=None, inframe=False):
+
+        self._locator_renderer = renderer
+
+        self._axes_class.draw(self, renderer, inframe)
+
+    def _make_twin_axes(self, *kl, **kwargs):
+        """
+        Need to overload so that twinx/twiny will work with
+        these axes.
+        """
+        if 'sharex' in kwargs and 'sharey' in kwargs:
+            raise ValueError("Twinned Axes may share only one axis.")
+        ax2 = type(self)(self.figure, self.get_position(True), *kl, **kwargs)
+        ax2.set_axes_locator(self.get_axes_locator())
+        self.figure.add_axes(ax2)
+        self.set_adjustable('datalim')
+        ax2.set_adjustable('datalim')
+        self._twinned_axes.join(self, ax2)
+        return ax2
+
+_locatableaxes_classes = {}
+
+
+def locatable_axes_factory(axes_class):
+
+    new_class = _locatableaxes_classes.get(axes_class)
+    if new_class is None:
+        new_class = type(str("Locatable%s" % (axes_class.__name__)),
+                         (LocatableAxesBase, axes_class),
+                         {'_axes_class': axes_class})
+
+        _locatableaxes_classes[axes_class] = new_class
+
+    return new_class
+
+#if hasattr(maxes.Axes, "get_axes_locator"):
+#    LocatableAxes = maxes.Axes
+#else:
+
+
+def make_axes_locatable(axes):
+    if not hasattr(axes, "set_axes_locator"):
+        new_class = locatable_axes_factory(type(axes))
+        axes.__class__ = new_class
+
+    divider = AxesDivider(axes)
+    locator = divider.new_locator(nx=0, ny=0)
+    axes.set_axes_locator(locator)
+
+    return divider
+
+
+def make_axes_area_auto_adjustable(ax,
+                                   use_axes=None, pad=0.1,
+                                   adjust_dirs=None):
+    if adjust_dirs is None:
+        adjust_dirs = ["left", "right", "bottom", "top"]
+    divider = make_axes_locatable(ax)
+
+    if use_axes is None:
+        use_axes = ax
+
+    divider.add_auto_adjustable_area(use_axes=use_axes, pad=pad,
+                                     adjust_dirs=adjust_dirs)
+
+#from matplotlib.axes import Axes
+from .mpl_axes import Axes
+LocatableAxes = locatable_axes_factory(Axes)
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_grid.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_grid.py
new file mode 100644
index 0000000000..dde0e8dd7c
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_grid.py
@@ -0,0 +1,771 @@
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import six
+
+import matplotlib.axes as maxes
+import matplotlib.cbook as cbook
+import matplotlib.ticker as ticker
+from matplotlib.gridspec import SubplotSpec
+
+from .axes_divider import Size, SubplotDivider, LocatableAxes, Divider
+from .colorbar import Colorbar
+
+
+def _extend_axes_pad(value):
+    # Check whether a list/tuple/array or scalar has been passed
+    ret = value
+    if not hasattr(ret, "__getitem__"):
+        ret = (value, value)
+    return ret
+
+
+def _tick_only(ax, bottom_on, left_on):
+    bottom_off = not bottom_on
+    left_off = not left_on
+    # [l.set_visible(bottom_off) for l in ax.get_xticklabels()]
+    # [l.set_visible(left_off) for l in ax.get_yticklabels()]
+    # ax.xaxis.label.set_visible(bottom_off)
+    # ax.yaxis.label.set_visible(left_off)
+    ax.axis["bottom"].toggle(ticklabels=bottom_off, label=bottom_off)
+    ax.axis["left"].toggle(ticklabels=left_off, label=left_off)
+
+
+class CbarAxesBase(object):
+
+    def colorbar(self, mappable, **kwargs):
+        locator = kwargs.pop("locator", None)
+
+        if locator is None:
+            if "ticks" not in kwargs:
+                kwargs["ticks"] = ticker.MaxNLocator(5)
+        if locator is not None:
+            if "ticks" in kwargs:
+                raise ValueError("Either *locator* or *ticks* need" +
+                                 " to be given, not both")
+            else:
+                kwargs["ticks"] = locator
+
+        self._hold = True
+        if self.orientation in ["top", "bottom"]:
+            orientation = "horizontal"
+        else:
+            orientation = "vertical"
+
+        cb = Colorbar(self, mappable, orientation=orientation, **kwargs)
+        self._config_axes()
+
+        def on_changed(m):
+            cb.set_cmap(m.get_cmap())
+            cb.set_clim(m.get_clim())
+            cb.update_bruteforce(m)
+
+        self.cbid = mappable.callbacksSM.connect('changed', on_changed)
+        mappable.colorbar = cb
+
+        self.locator = cb.cbar_axis.get_major_locator()
+
+        return cb
+
+    def _config_axes(self):
+        '''
+        Make an axes patch and outline.
+        '''
+        ax = self
+        ax.set_navigate(False)
+
+        ax.axis[:].toggle(all=False)
+        b = self._default_label_on
+        ax.axis[self.orientation].toggle(all=b)
+
+        # for axis in ax.axis.values():
+        #     axis.major_ticks.set_visible(False)
+        #     axis.minor_ticks.set_visible(False)
+        #     axis.major_ticklabels.set_visible(False)
+        #     axis.minor_ticklabels.set_visible(False)
+        #     axis.label.set_visible(False)
+
+        # axis = ax.axis[self.orientation]
+        # axis.major_ticks.set_visible(True)
+        # axis.minor_ticks.set_visible(True)
+
+        #axis.major_ticklabels.set_size(
+        #    int(axis.major_ticklabels.get_size()*.9))
+        #axis.major_tick_pad = 3
+
+        # axis.major_ticklabels.set_visible(b)
+        # axis.minor_ticklabels.set_visible(b)
+        # axis.label.set_visible(b)
+
+    def toggle_label(self, b):
+        self._default_label_on = b
+        axis = self.axis[self.orientation]
+        axis.toggle(ticklabels=b, label=b)
+        #axis.major_ticklabels.set_visible(b)
+        #axis.minor_ticklabels.set_visible(b)
+        #axis.label.set_visible(b)
+
+
+class CbarAxes(CbarAxesBase, LocatableAxes):
+    def __init__(self, *kl, **kwargs):
+        orientation = kwargs.pop("orientation", None)
+        if orientation is None:
+            raise ValueError("orientation must be specified")
+        self.orientation = orientation
+        self._default_label_on = True
+        self.locator = None
+
+        super(LocatableAxes, self).__init__(*kl, **kwargs)
+
+    def cla(self):
+        super(LocatableAxes, self).cla()
+        self._config_axes()
+
+
+class Grid(object):
+    """
+    A class that creates a grid of Axes. In matplotlib, the axes
+    location (and size) is specified in the normalized figure
+    coordinates. This may not be ideal for images that needs to be
+    displayed with a given aspect ratio.  For example, displaying
+    images of a same size with some fixed padding between them cannot
+    be easily done in matplotlib. AxesGrid is used in such case.
+    """
+
+    _defaultLocatableAxesClass = LocatableAxes
+
+    def __init__(self, fig,
+                 rect,
+                 nrows_ncols,
+                 ngrids=None,
+                 direction="row",
+                 axes_pad=0.02,
+                 add_all=True,
+                 share_all=False,
+                 share_x=True,
+                 share_y=True,
+                 #aspect=True,
+                 label_mode="L",
+                 axes_class=None,
+                 ):
+        """
+        Build an :class:`Grid` instance with a grid nrows*ncols
+        :class:`~matplotlib.axes.Axes` in
+        :class:`~matplotlib.figure.Figure` *fig* with
+        *rect=[left, bottom, width, height]* (in
+        :class:`~matplotlib.figure.Figure` coordinates) or
+        the subplot position code (e.g., "121").
+
+        Optional keyword arguments:
+
+          ================  ========  =========================================
+          Keyword           Default   Description
+          ================  ========  =========================================
+          direction         "row"     [ "row" | "column" ]
+          axes_pad          0.02      float| pad between axes given in inches
+                                      or tuple-like of floats,
+                                      (horizontal padding, vertical padding)
+          add_all           True      bool
+          share_all         False     bool
+          share_x           True      bool
+          share_y           True      bool
+          label_mode        "L"       [ "L" | "1" | "all" ]
+          axes_class        None      a type object which must be a subclass
+                                      of :class:`~matplotlib.axes.Axes`
+          ================  ========  =========================================
+        """
+        self._nrows, self._ncols = nrows_ncols
+
+        if ngrids is None:
+            ngrids = self._nrows * self._ncols
+        else:
+            if (ngrids > self._nrows * self._ncols) or (ngrids <= 0):
+                raise Exception("")
+
+        self.ngrids = ngrids
+
+        self._init_axes_pad(axes_pad)
+
+        if direction not in ["column", "row"]:
+            raise Exception("")
+
+        self._direction = direction
+
+        if axes_class is None:
+            axes_class = self._defaultLocatableAxesClass
+            axes_class_args = {}
+        else:
+            if (type(axes_class)) == type and \
+                   issubclass(axes_class,
+                              self._defaultLocatableAxesClass.Axes):
+                axes_class_args = {}
+            else:
+                axes_class, axes_class_args = axes_class
+
+        self.axes_all = []
+        self.axes_column = [[] for _ in range(self._ncols)]
+        self.axes_row = [[] for _ in range(self._nrows)]
+
+        h = []
+        v = []
+        if isinstance(rect, six.string_types) or cbook.is_numlike(rect):
+            self._divider = SubplotDivider(fig, rect, horizontal=h, vertical=v,
+                                           aspect=False)
+        elif isinstance(rect, SubplotSpec):
+            self._divider = SubplotDivider(fig, rect, horizontal=h, vertical=v,
+                                           aspect=False)
+        elif len(rect) == 3:
+            kw = dict(horizontal=h, vertical=v, aspect=False)
+            self._divider = SubplotDivider(fig, *rect, **kw)
+        elif len(rect) == 4:
+            self._divider = Divider(fig, rect, horizontal=h, vertical=v,
+                                    aspect=False)
+        else:
+            raise Exception("")
+
+        rect = self._divider.get_position()
+
+        # reference axes
+        self._column_refax = [None for _ in range(self._ncols)]
+        self._row_refax = [None for _ in range(self._nrows)]
+        self._refax = None
+
+        for i in range(self.ngrids):
+
+            col, row = self._get_col_row(i)
+
+            if share_all:
+                sharex = self._refax
+                sharey = self._refax
+            else:
+                if share_x:
+                    sharex = self._column_refax[col]
+                else:
+                    sharex = None
+
+                if share_y:
+                    sharey = self._row_refax[row]
+                else:
+                    sharey = None
+
+            ax = axes_class(fig, rect, sharex=sharex, sharey=sharey,
+                            **axes_class_args)
+
+            if share_all:
+                if self._refax is None:
+                    self._refax = ax
+            else:
+                if sharex is None:
+                    self._column_refax[col] = ax
+                if sharey is None:
+                    self._row_refax[row] = ax
+
+            self.axes_all.append(ax)
+            self.axes_column[col].append(ax)
+            self.axes_row[row].append(ax)
+
+        self.axes_llc = self.axes_column[0][-1]
+
+        self._update_locators()
+
+        if add_all:
+            for ax in self.axes_all:
+                fig.add_axes(ax)
+
+        self.set_label_mode(label_mode)
+
+    def _init_axes_pad(self, axes_pad):
+        axes_pad = _extend_axes_pad(axes_pad)
+        self._axes_pad = axes_pad
+
+        self._horiz_pad_size = Size.Fixed(axes_pad[0])
+        self._vert_pad_size = Size.Fixed(axes_pad[1])
+
+    def _update_locators(self):
+
+        h = []
+
+        h_ax_pos = []
+
+        for _ in self._column_refax:
+            #if h: h.append(Size.Fixed(self._axes_pad))
+            if h:
+                h.append(self._horiz_pad_size)
+
+            h_ax_pos.append(len(h))
+
+            sz = Size.Scaled(1)
+            h.append(sz)
+
+        v = []
+
+        v_ax_pos = []
+        for _ in self._row_refax[::-1]:
+            #if v: v.append(Size.Fixed(self._axes_pad))
+            if v:
+                v.append(self._vert_pad_size)
+
+            v_ax_pos.append(len(v))
+            sz = Size.Scaled(1)
+            v.append(sz)
+
+        for i in range(self.ngrids):
+            col, row = self._get_col_row(i)
+            locator = self._divider.new_locator(nx=h_ax_pos[col],
+                                ny=v_ax_pos[self._nrows - 1 - row])
+            self.axes_all[i].set_axes_locator(locator)
+
+        self._divider.set_horizontal(h)
+        self._divider.set_vertical(v)
+
+    def _get_col_row(self, n):
+        if self._direction == "column":
+            col, row = divmod(n, self._nrows)
+        else:
+            row, col = divmod(n, self._ncols)
+
+        return col, row
+
+    # Good to propagate __len__ if we have __getitem__
+    def __len__(self):
+        return len(self.axes_all)
+
+    def __getitem__(self, i):
+        return self.axes_all[i]
+
+    def get_geometry(self):
+        """
+        get geometry of the grid. Returns a tuple of two integer,
+        representing number of rows and number of columns.
+        """
+        return self._nrows, self._ncols
+
+    def set_axes_pad(self, axes_pad):
+        "set axes_pad"
+        self._axes_pad = axes_pad
+
+        # These two lines actually differ from ones in _init_axes_pad
+        self._horiz_pad_size.fixed_size = axes_pad[0]
+        self._vert_pad_size.fixed_size = axes_pad[1]
+
+    def get_axes_pad(self):
+        """
+        get axes_pad
+
+        Returns
+        -------
+        tuple
+            Padding in inches, (horizontal pad, vertical pad)
+        """
+        return self._axes_pad
+
+    def set_aspect(self, aspect):
+        "set aspect"
+        self._divider.set_aspect(aspect)
+
+    def get_aspect(self):
+        "get aspect"
+        return self._divider.get_aspect()
+
+    def set_label_mode(self, mode):
+        "set label_mode"
+        if mode == "all":
+            for ax in self.axes_all:
+                _tick_only(ax, False, False)
+        elif mode == "L":
+            # left-most axes
+            for ax in self.axes_column[0][:-1]:
+                _tick_only(ax, bottom_on=True, left_on=False)
+            # lower-left axes
+            ax = self.axes_column[0][-1]
+            _tick_only(ax, bottom_on=False, left_on=False)
+
+            for col in self.axes_column[1:]:
+                # axes with no labels
+                for ax in col[:-1]:
+                    _tick_only(ax, bottom_on=True, left_on=True)
+
+                # bottom
+                ax = col[-1]
+                _tick_only(ax, bottom_on=False, left_on=True)
+
+        elif mode == "1":
+            for ax in self.axes_all:
+                _tick_only(ax, bottom_on=True, left_on=True)
+
+            ax = self.axes_llc
+            _tick_only(ax, bottom_on=False, left_on=False)
+
+    def get_divider(self):
+        return self._divider
+
+    def set_axes_locator(self, locator):
+        self._divider.set_locator(locator)
+
+    def get_axes_locator(self):
+        return self._divider.get_locator()
+
+    def get_vsize_hsize(self):
+
+        return self._divider.get_vsize_hsize()
+#         from axes_size import AddList
+
+#         vsize = AddList(self._divider.get_vertical())
+#         hsize = AddList(self._divider.get_horizontal())
+
+#         return vsize, hsize
+
+
+class ImageGrid(Grid):
+    """
+    A class that creates a grid of Axes. In matplotlib, the axes
+    location (and size) is specified in the normalized figure
+    coordinates. This may not be ideal for images that needs to be
+    displayed with a given aspect ratio.  For example, displaying
+    images of a same size with some fixed padding between them cannot
+    be easily done in matplotlib. ImageGrid is used in such case.
+    """
+
+    _defaultCbarAxesClass = CbarAxes
+
+    def __init__(self, fig,
+                 rect,
+                 nrows_ncols,
+                 ngrids=None,
+                 direction="row",
+                 axes_pad=0.02,
+                 add_all=True,
+                 share_all=False,
+                 aspect=True,
+                 label_mode="L",
+                 cbar_mode=None,
+                 cbar_location="right",
+                 cbar_pad=None,
+                 cbar_size="5%",
+                 cbar_set_cax=True,
+                 axes_class=None,
+                 ):
+        """
+        Build an :class:`ImageGrid` instance with a grid nrows*ncols
+        :class:`~matplotlib.axes.Axes` in
+        :class:`~matplotlib.figure.Figure` *fig* with
+        *rect=[left, bottom, width, height]* (in
+        :class:`~matplotlib.figure.Figure` coordinates) or
+        the subplot position code (e.g., "121").
+
+        Optional keyword arguments:
+
+          ================  ========  =========================================
+          Keyword           Default   Description
+          ================  ========  =========================================
+          direction         "row"     [ "row" | "column" ]
+          axes_pad          0.02      float| pad between axes given in inches
+                                      or tuple-like of floats,
+                                      (horizontal padding, vertical padding)
+          add_all           True      bool
+          share_all         False     bool
+          aspect            True      bool
+          label_mode        "L"       [ "L" | "1" | "all" ]
+          cbar_mode         None      [ "each" | "single" | "edge" ]
+          cbar_location     "right"   [ "left" | "right" | "bottom" | "top" ]
+          cbar_pad          None
+          cbar_size         "5%"
+          cbar_set_cax      True      bool
+          axes_class        None      a type object which must be a subclass
+                                      of axes_grid's subclass of
+                                      :class:`~matplotlib.axes.Axes`
+          ================  ========  =========================================
+
+        *cbar_set_cax* : if True, each axes in the grid has a cax
+          attribute that is bind to associated cbar_axes.
+        """
+        self._nrows, self._ncols = nrows_ncols
+
+        if ngrids is None:
+            ngrids = self._nrows * self._ncols
+        else:
+            if not 0 <= ngrids < self._nrows * self._ncols:
+                raise Exception
+
+        self.ngrids = ngrids
+
+        axes_pad = _extend_axes_pad(axes_pad)
+        self._axes_pad = axes_pad
+
+        self._colorbar_mode = cbar_mode
+        self._colorbar_location = cbar_location
+        if cbar_pad is None:
+            # horizontal or vertical arrangement?
+            if cbar_location in ("left", "right"):
+                self._colorbar_pad = axes_pad[0]
+            else:
+                self._colorbar_pad = axes_pad[1]
+        else:
+            self._colorbar_pad = cbar_pad
+
+        self._colorbar_size = cbar_size
+
+        self._init_axes_pad(axes_pad)
+
+        if direction not in ["column", "row"]:
+            raise Exception("")
+
+        self._direction = direction
+
+        if axes_class is None:
+            axes_class = self._defaultLocatableAxesClass
+            axes_class_args = {}
+        else:
+            if isinstance(axes_class, maxes.Axes):
+                axes_class_args = {}
+            else:
+                axes_class, axes_class_args = axes_class
+
+        self.axes_all = []
+        self.axes_column = [[] for _ in range(self._ncols)]
+        self.axes_row = [[] for _ in range(self._nrows)]
+
+        self.cbar_axes = []
+
+        h = []
+        v = []
+        if isinstance(rect, six.string_types) or cbook.is_numlike(rect):
+            self._divider = SubplotDivider(fig, rect, horizontal=h, vertical=v,
+                                           aspect=aspect)
+        elif isinstance(rect, SubplotSpec):
+            self._divider = SubplotDivider(fig, rect, horizontal=h, vertical=v,
+                                           aspect=aspect)
+        elif len(rect) == 3:
+            kw = dict(horizontal=h, vertical=v, aspect=aspect)
+            self._divider = SubplotDivider(fig, *rect, **kw)
+        elif len(rect) == 4:
+            self._divider = Divider(fig, rect, horizontal=h, vertical=v,
+                                    aspect=aspect)
+        else:
+            raise Exception("")
+
+        rect = self._divider.get_position()
+
+        # reference axes
+        self._column_refax = [None for _ in range(self._ncols)]
+        self._row_refax = [None for _ in range(self._nrows)]
+        self._refax = None
+
+        for i in range(self.ngrids):
+
+            col, row = self._get_col_row(i)
+
+            if share_all:
+                if self.axes_all:
+                    sharex = self.axes_all[0]
+                    sharey = self.axes_all[0]
+                else:
+                    sharex = None
+                    sharey = None
+            else:
+                sharex = self._column_refax[col]
+                sharey = self._row_refax[row]
+
+            ax = axes_class(fig, rect, sharex=sharex, sharey=sharey,
+                            **axes_class_args)
+
+            self.axes_all.append(ax)
+            self.axes_column[col].append(ax)
+            self.axes_row[row].append(ax)
+
+            if share_all:
+                if self._refax is None:
+                    self._refax = ax
+            if sharex is None:
+                self._column_refax[col] = ax
+            if sharey is None:
+                self._row_refax[row] = ax
+
+            cax = self._defaultCbarAxesClass(fig, rect,
+                                        orientation=self._colorbar_location)
+            self.cbar_axes.append(cax)
+
+        self.axes_llc = self.axes_column[0][-1]
+
+        self._update_locators()
+
+        if add_all:
+            for ax in self.axes_all+self.cbar_axes:
+                fig.add_axes(ax)
+
+        if cbar_set_cax:
+            if self._colorbar_mode == "single":
+                for ax in self.axes_all:
+                    ax.cax = self.cbar_axes[0]
+            elif self._colorbar_mode == "edge":
+                for index, ax in enumerate(self.axes_all):
+                    col, row = self._get_col_row(index)
+                    if self._colorbar_location in ("left", "right"):
+                        ax.cax = self.cbar_axes[row]
+                    else:
+                        ax.cax = self.cbar_axes[col]
+            else:
+                for ax, cax in zip(self.axes_all, self.cbar_axes):
+                    ax.cax = cax
+
+        self.set_label_mode(label_mode)
+
+    def _update_locators(self):
+
+        h = []
+        v = []
+
+        h_ax_pos = []
+        h_cb_pos = []
+        if (self._colorbar_mode == "single" and
+             self._colorbar_location in ('left', 'bottom')):
+            if self._colorbar_location == "left":
+                #sz = Size.Fraction(Size.AxesX(self.axes_llc), self._nrows)
+                sz = Size.Fraction(self._nrows, Size.AxesX(self.axes_llc))
+                h.append(Size.from_any(self._colorbar_size, sz))
+                h.append(Size.from_any(self._colorbar_pad, sz))
+                locator = self._divider.new_locator(nx=0, ny=0, ny1=-1)
+            elif self._colorbar_location == "bottom":
+                #sz = Size.Fraction(Size.AxesY(self.axes_llc), self._ncols)
+                sz = Size.Fraction(self._ncols, Size.AxesY(self.axes_llc))
+                v.append(Size.from_any(self._colorbar_size, sz))
+                v.append(Size.from_any(self._colorbar_pad, sz))
+                locator = self._divider.new_locator(nx=0, nx1=-1, ny=0)
+            for i in range(self.ngrids):
+                self.cbar_axes[i].set_visible(False)
+            self.cbar_axes[0].set_axes_locator(locator)
+            self.cbar_axes[0].set_visible(True)
+
+        for col, ax in enumerate(self.axes_row[0]):
+            if h:
+                h.append(self._horiz_pad_size)  # Size.Fixed(self._axes_pad))
+
+            if ax:
+                sz = Size.AxesX(ax, aspect="axes", ref_ax=self.axes_all[0])
+            else:
+                sz = Size.AxesX(self.axes_all[0],
+                                aspect="axes", ref_ax=self.axes_all[0])
+
+            if (self._colorbar_mode == "each" or
+                    (self._colorbar_mode == 'edge' and
+                        col == 0)) and self._colorbar_location == "left":
+                h_cb_pos.append(len(h))
+                h.append(Size.from_any(self._colorbar_size, sz))
+                h.append(Size.from_any(self._colorbar_pad, sz))
+
+            h_ax_pos.append(len(h))
+
+            h.append(sz)
+
+            if ((self._colorbar_mode == "each" or
+                    (self._colorbar_mode == 'edge' and
+                        col == self._ncols - 1)) and
+                    self._colorbar_location == "right"):
+                h.append(Size.from_any(self._colorbar_pad, sz))
+                h_cb_pos.append(len(h))
+                h.append(Size.from_any(self._colorbar_size, sz))
+
+        v_ax_pos = []
+        v_cb_pos = []
+        for row, ax in enumerate(self.axes_column[0][::-1]):
+            if v:
+                v.append(self._vert_pad_size)  # Size.Fixed(self._axes_pad))
+
+            if ax:
+                sz = Size.AxesY(ax, aspect="axes", ref_ax=self.axes_all[0])
+            else:
+                sz = Size.AxesY(self.axes_all[0],
+                                aspect="axes", ref_ax=self.axes_all[0])
+
+            if (self._colorbar_mode == "each" or
+                    (self._colorbar_mode == 'edge' and
+                        row == 0)) and self._colorbar_location == "bottom":
+                v_cb_pos.append(len(v))
+                v.append(Size.from_any(self._colorbar_size, sz))
+                v.append(Size.from_any(self._colorbar_pad, sz))
+
+            v_ax_pos.append(len(v))
+            v.append(sz)
+
+            if ((self._colorbar_mode == "each" or
+                    (self._colorbar_mode == 'edge' and
+                        row == self._nrows - 1)) and
+                        self._colorbar_location == "top"):
+                v.append(Size.from_any(self._colorbar_pad, sz))
+                v_cb_pos.append(len(v))
+                v.append(Size.from_any(self._colorbar_size, sz))
+
+        for i in range(self.ngrids):
+            col, row = self._get_col_row(i)
+            #locator = self._divider.new_locator(nx=4*col,
+            #                                    ny=2*(self._nrows - row - 1))
+            locator = self._divider.new_locator(nx=h_ax_pos[col],
+                                                ny=v_ax_pos[self._nrows-1-row])
+            self.axes_all[i].set_axes_locator(locator)
+
+            if self._colorbar_mode == "each":
+                if self._colorbar_location in ("right", "left"):
+                    locator = self._divider.new_locator(
+                        nx=h_cb_pos[col], ny=v_ax_pos[self._nrows - 1 - row])
+
+                elif self._colorbar_location in ("top", "bottom"):
+                    locator = self._divider.new_locator(
+                        nx=h_ax_pos[col], ny=v_cb_pos[self._nrows - 1 - row])
+
+                self.cbar_axes[i].set_axes_locator(locator)
+            elif self._colorbar_mode == 'edge':
+                if ((self._colorbar_location == 'left' and col == 0) or
+                        (self._colorbar_location == 'right'
+                         and col == self._ncols-1)):
+                    locator = self._divider.new_locator(
+                        nx=h_cb_pos[0], ny=v_ax_pos[self._nrows -1 - row])
+                    self.cbar_axes[row].set_axes_locator(locator)
+                elif ((self._colorbar_location == 'bottom' and
+                       row == self._nrows - 1) or
+                        (self._colorbar_location == 'top' and row == 0)):
+                    locator = self._divider.new_locator(nx=h_ax_pos[col],
+                                                        ny=v_cb_pos[0])
+                    self.cbar_axes[col].set_axes_locator(locator)
+
+        if self._colorbar_mode == "single":
+            if self._colorbar_location == "right":
+                #sz = Size.Fraction(Size.AxesX(self.axes_llc), self._nrows)
+                sz = Size.Fraction(self._nrows, Size.AxesX(self.axes_llc))
+                h.append(Size.from_any(self._colorbar_pad, sz))
+                h.append(Size.from_any(self._colorbar_size, sz))
+                locator = self._divider.new_locator(nx=-2, ny=0, ny1=-1)
+            elif self._colorbar_location == "top":
+                #sz = Size.Fraction(Size.AxesY(self.axes_llc), self._ncols)
+                sz = Size.Fraction(self._ncols, Size.AxesY(self.axes_llc))
+                v.append(Size.from_any(self._colorbar_pad, sz))
+                v.append(Size.from_any(self._colorbar_size, sz))
+                locator = self._divider.new_locator(nx=0, nx1=-1, ny=-2)
+            if self._colorbar_location in ("right", "top"):
+                for i in range(self.ngrids):
+                    self.cbar_axes[i].set_visible(False)
+                self.cbar_axes[0].set_axes_locator(locator)
+                self.cbar_axes[0].set_visible(True)
+        elif self._colorbar_mode == "each":
+            for i in range(self.ngrids):
+                self.cbar_axes[i].set_visible(True)
+        elif self._colorbar_mode == "edge":
+            if self._colorbar_location in ('right', 'left'):
+                count = self._nrows
+            else:
+                count = self._ncols
+            for i in range(count):
+                self.cbar_axes[i].set_visible(True)
+            for j in range(i + 1, self.ngrids):
+                self.cbar_axes[j].set_visible(False)
+        else:
+            for i in range(self.ngrids):
+                self.cbar_axes[i].set_visible(False)
+                self.cbar_axes[i].set_position([1., 1., 0.001, 0.001],
+                                               which="active")
+
+        self._divider.set_horizontal(h)
+        self._divider.set_vertical(v)
+
+
+AxesGrid = ImageGrid
+
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_rgb.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_rgb.py
new file mode 100644
index 0000000000..e62d4f0615
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_rgb.py
@@ -0,0 +1,228 @@
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import six
+
+import numpy as np
+from .axes_divider import make_axes_locatable, Size, locatable_axes_factory
+import sys
+from .mpl_axes import Axes
+
+
+def make_rgb_axes(ax, pad=0.01, axes_class=None, add_all=True):
+    """
+    pad : fraction of the axes height.
+    """
+
+    divider = make_axes_locatable(ax)
+
+    pad_size = Size.Fraction(pad, Size.AxesY(ax))
+
+    xsize = Size.Fraction((1.-2.*pad)/3., Size.AxesX(ax))
+    ysize = Size.Fraction((1.-2.*pad)/3., Size.AxesY(ax))
+
+    divider.set_horizontal([Size.AxesX(ax), pad_size, xsize])
+    divider.set_vertical([ysize, pad_size, ysize, pad_size, ysize])
+
+    ax.set_axes_locator(divider.new_locator(0, 0, ny1=-1))
+
+    ax_rgb = []
+    if axes_class is None:
+        try:
+            axes_class = locatable_axes_factory(ax._axes_class)
+        except AttributeError:
+            axes_class = locatable_axes_factory(type(ax))
+
+    for ny in [4, 2, 0]:
+        ax1 = axes_class(ax.get_figure(),
+                         ax.get_position(original=True),
+                         sharex=ax, sharey=ax)
+        locator = divider.new_locator(nx=2, ny=ny)
+        ax1.set_axes_locator(locator)
+        for t in ax1.yaxis.get_ticklabels() + ax1.xaxis.get_ticklabels():
+            t.set_visible(False)
+        try:
+            for axis in ax1.axis.values():
+                axis.major_ticklabels.set_visible(False)
+        except AttributeError:
+            pass
+
+        ax_rgb.append(ax1)
+
+    if add_all:
+        fig = ax.get_figure()
+        for ax1 in ax_rgb:
+            fig.add_axes(ax1)
+
+    return ax_rgb
+
+
+def imshow_rgb(ax, r, g, b, **kwargs):
+    ny, nx = r.shape
+    R = np.zeros([ny, nx, 3], dtype="d")
+    R[:,:,0] = r
+    G = np.zeros_like(R)
+    G[:,:,1] = g
+    B = np.zeros_like(R)
+    B[:,:,2] = b
+
+    RGB = R + G + B
+
+    im_rgb = ax.imshow(RGB, **kwargs)
+
+    return im_rgb
+
+
+class RGBAxesBase(object):
+    """base class for a 4-panel imshow (RGB, R, G, B)
+
+    Layout:
+    +---------------+-----+
+    |               |  R  |
+    +               +-----+
+    |      RGB      |  G  |
+    +               +-----+
+    |               |  B  |
+    +---------------+-----+
+
+    Attributes
+    ----------
+    _defaultAxesClass : matplotlib.axes.Axes
+        defaults to 'Axes' in RGBAxes child class.
+        No default in abstract base class
+    RGB : _defaultAxesClass
+        The axes object for the three-channel imshow
+    R : _defaultAxesClass
+        The axes object for the red channel imshow
+    G : _defaultAxesClass
+        The axes object for the green channel imshow
+    B : _defaultAxesClass
+        The axes object for the blue channel imshow
+    """
+    def __init__(self, *kl, **kwargs):
+        """
+        Parameters
+        ----------
+        pad : float
+            fraction of the axes height to put as padding.
+            defaults to 0.0
+        add_all : bool
+            True: Add the {rgb, r, g, b} axes to the figure
+            defaults to True.
+        axes_class : matplotlib.axes.Axes
+
+        kl :
+            Unpacked into axes_class() init for RGB
+        kwargs :
+            Unpacked into axes_class() init for RGB, R, G, B axes
+        """
+        pad = kwargs.pop("pad", 0.0)
+        add_all = kwargs.pop("add_all", True)
+        try:
+            axes_class = kwargs.pop("axes_class", self._defaultAxesClass)
+        except AttributeError:
+            new_msg = ("A subclass of RGBAxesBase must have a "
+                       "_defaultAxesClass attribute. If you are not sure which "
+                       "axes class to use, consider using "
+                       "mpl_toolkits.axes_grid1.mpl_axes.Axes.")
+            six.reraise(AttributeError, AttributeError(new_msg),
+                        sys.exc_info()[2])
+
+        ax = axes_class(*kl, **kwargs)
+
+        divider = make_axes_locatable(ax)
+
+        pad_size = Size.Fraction(pad, Size.AxesY(ax))
+
+        xsize = Size.Fraction((1.-2.*pad)/3., Size.AxesX(ax))
+        ysize = Size.Fraction((1.-2.*pad)/3., Size.AxesY(ax))
+
+        divider.set_horizontal([Size.AxesX(ax), pad_size, xsize])
+        divider.set_vertical([ysize, pad_size, ysize, pad_size, ysize])
+
+        ax.set_axes_locator(divider.new_locator(0, 0, ny1=-1))
+
+        ax_rgb = []
+        for ny in [4, 2, 0]:
+            ax1 = axes_class(ax.get_figure(),
+                             ax.get_position(original=True),
+                             sharex=ax, sharey=ax, **kwargs)
+            locator = divider.new_locator(nx=2, ny=ny)
+            ax1.set_axes_locator(locator)
+            ax1.axis[:].toggle(ticklabels=False)
+            ax_rgb.append(ax1)
+
+        self.RGB = ax
+        self.R, self.G, self.B = ax_rgb
+
+        if add_all:
+            fig = ax.get_figure()
+            fig.add_axes(ax)
+            self.add_RGB_to_figure()
+
+        self._config_axes()
+
+    def _config_axes(self, line_color='w', marker_edge_color='w'):
+        """Set the line color and ticks for the axes
+
+        Parameters
+        ----------
+        line_color : any matplotlib color
+        marker_edge_color : any matplotlib color
+        """
+        for ax1 in [self.RGB, self.R, self.G, self.B]:
+            ax1.axis[:].line.set_color(line_color)
+            ax1.axis[:].major_ticks.set_markeredgecolor(marker_edge_color)
+
+    def add_RGB_to_figure(self):
+        """Add the red, green and blue axes to the RGB composite's axes figure
+        """
+        self.RGB.get_figure().add_axes(self.R)
+        self.RGB.get_figure().add_axes(self.G)
+        self.RGB.get_figure().add_axes(self.B)
+
+    def imshow_rgb(self, r, g, b, **kwargs):
+        """Create the four images {rgb, r, g, b}
+
+        Parameters
+        ----------
+        r : array-like
+            The red array
+        g : array-like
+            The green array
+        b : array-like
+            The blue array
+        kwargs : imshow kwargs
+            kwargs get unpacked into the imshow calls for the four images
+
+        Returns
+        -------
+        rgb : matplotlib.image.AxesImage
+        r : matplotlib.image.AxesImage
+        g : matplotlib.image.AxesImage
+        b : matplotlib.image.AxesImage
+        """
+        if not (r.shape == g.shape == b.shape):
+            raise ValueError('Input shapes do not match.'
+                             '\nr.shape = {}'
+                             '\ng.shape = {}'
+                             '\nb.shape = {}'
+                             .format(r.shape, g.shape, b.shape))
+        RGB = np.dstack([r, g, b])
+        R = np.zeros_like(RGB)
+        R[:,:,0] = r
+        G = np.zeros_like(RGB)
+        G[:,:,1] = g
+        B = np.zeros_like(RGB)
+        B[:,:,2] = b
+
+        im_rgb = self.RGB.imshow(RGB, **kwargs)
+        im_r = self.R.imshow(R, **kwargs)
+        im_g = self.G.imshow(G, **kwargs)
+        im_b = self.B.imshow(B, **kwargs)
+
+        return im_rgb, im_r, im_g, im_b
+
+
+class RGBAxes(RGBAxesBase):
+    _defaultAxesClass = Axes
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_size.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_size.py
new file mode 100644
index 0000000000..163a6245fe
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_size.py
@@ -0,0 +1,323 @@
+
+"""
+provides a classes of simple units that will be used with AxesDivider
+class (or others) to determine the size of each axes. The unit
+classes define `get_size` method that returns a tuple of two floats,
+meaning relative and absolute sizes, respectively.
+
+Note that this class is nothing more than a simple tuple of two
+floats. Take a look at the Divider class to see how these two
+values are used.
+
+"""
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import six
+
+import matplotlib.cbook as cbook
+from matplotlib.axes import Axes
+
+class _Base(object):
+    "Base class"
+
+    def __rmul__(self, other):
+        float(other) # just to check if number if given
+        return Fraction(other, self)
+
+    def __add__(self, other):
+        if isinstance(other, _Base):
+            return Add(self, other)
+        else:
+            float(other)
+            other = Fixed(other)
+            return Add(self, other)
+
+
+class Add(_Base):
+    def __init__(self, a, b):
+        self._a = a
+        self._b = b
+
+    def get_size(self, renderer):
+        a_rel_size, a_abs_size = self._a.get_size(renderer)
+        b_rel_size, b_abs_size = self._b.get_size(renderer)
+        return a_rel_size + b_rel_size, a_abs_size + b_abs_size
+
+class AddList(_Base):
+    def __init__(self, add_list):
+        self._list = add_list
+
+    def get_size(self, renderer):
+        sum_rel_size = sum([a.get_size(renderer)[0] for a in self._list])
+        sum_abs_size = sum([a.get_size(renderer)[1] for a in self._list])
+        return sum_rel_size, sum_abs_size
+
+
+class Fixed(_Base):
+    "Simple fixed size  with absolute part = *fixed_size* and relative part = 0"
+    def __init__(self, fixed_size):
+        self.fixed_size = fixed_size
+
+    def get_size(self, renderer):
+        rel_size = 0.
+        abs_size = self.fixed_size
+        return rel_size, abs_size
+
+
+class Scaled(_Base):
+    "Simple scaled(?) size with absolute part = 0 and relative part = *scalable_size*"
+    def __init__(self, scalable_size):
+        self._scalable_size = scalable_size
+
+    def get_size(self, renderer):
+        rel_size = self._scalable_size
+        abs_size = 0.
+        return rel_size, abs_size
+
+Scalable=Scaled
+
+def _get_axes_aspect(ax):
+    aspect = ax.get_aspect()
+    # when aspec is "auto", consider it as 1.
+    if aspect in ('normal', 'auto'):
+        aspect = 1.
+    elif aspect == "equal":
+        aspect = 1
+    else:
+        aspect = float(aspect)
+
+    return aspect
+
+class AxesX(_Base):
+    """
+    Scaled size whose relative part corresponds to the data width
+    of the *axes* multiplied by the *aspect*.
+    """
+    def __init__(self, axes, aspect=1., ref_ax=None):
+        self._axes = axes
+        self._aspect = aspect
+        if aspect == "axes" and ref_ax is None:
+            raise ValueError("ref_ax must be set when aspect='axes'")
+        self._ref_ax = ref_ax
+
+    def get_size(self, renderer):
+        l1, l2 = self._axes.get_xlim()
+        if self._aspect == "axes":
+            ref_aspect = _get_axes_aspect(self._ref_ax)
+            aspect = ref_aspect/_get_axes_aspect(self._axes)
+        else:
+            aspect = self._aspect
+
+        rel_size = abs(l2-l1)*aspect
+        abs_size = 0.
+        return rel_size, abs_size
+
+class AxesY(_Base):
+    """
+    Scaled size whose relative part corresponds to the data height
+    of the *axes* multiplied by the *aspect*.
+    """
+    def __init__(self, axes, aspect=1., ref_ax=None):
+        self._axes = axes
+        self._aspect = aspect
+        if aspect == "axes" and ref_ax is None:
+            raise ValueError("ref_ax must be set when aspect='axes'")
+        self._ref_ax = ref_ax
+
+    def get_size(self, renderer):
+        l1, l2 = self._axes.get_ylim()
+
+        if self._aspect == "axes":
+            ref_aspect = _get_axes_aspect(self._ref_ax)
+            aspect = _get_axes_aspect(self._axes)
+        else:
+            aspect = self._aspect
+
+        rel_size = abs(l2-l1)*aspect
+        abs_size = 0.
+        return rel_size, abs_size
+
+
+class MaxExtent(_Base):
+    """
+    Size whose absolute part is the largest width (or height) of
+    the given *artist_list*.
+    """
+    def __init__(self, artist_list, w_or_h):
+        self._artist_list = artist_list
+
+        if w_or_h not in ["width", "height"]:
+            raise ValueError()
+
+        self._w_or_h = w_or_h
+
+    def add_artist(self, a):
+        self._artist_list.append(a)
+
+    def get_size(self, renderer):
+        rel_size = 0.
+        w_list, h_list = [], []
+        for a in self._artist_list:
+            bb = a.get_window_extent(renderer)
+            w_list.append(bb.width)
+            h_list.append(bb.height)
+        dpi = a.get_figure().get_dpi()
+        if self._w_or_h == "width":
+            abs_size = max(w_list)/dpi
+        elif self._w_or_h == "height":
+            abs_size = max(h_list)/dpi
+
+        return rel_size, abs_size
+
+
+class MaxWidth(_Base):
+    """
+    Size whose absolute part is the largest width of
+    the given *artist_list*.
+    """
+    def __init__(self, artist_list):
+        self._artist_list = artist_list
+
+    def add_artist(self, a):
+        self._artist_list.append(a)
+
+    def get_size(self, renderer):
+        rel_size = 0.
+        w_list = []
+        for a in self._artist_list:
+            bb = a.get_window_extent(renderer)
+            w_list.append(bb.width)
+        dpi = a.get_figure().get_dpi()
+        abs_size = max(w_list)/dpi
+
+        return rel_size, abs_size
+
+
+
+class MaxHeight(_Base):
+    """
+    Size whose absolute part is the largest height of
+    the given *artist_list*.
+    """
+    def __init__(self, artist_list):
+        self._artist_list = artist_list
+
+    def add_artist(self, a):
+        self._artist_list.append(a)
+
+    def get_size(self, renderer):
+        rel_size = 0.
+        h_list = []
+        for a in self._artist_list:
+            bb = a.get_window_extent(renderer)
+            h_list.append(bb.height)
+        dpi = a.get_figure().get_dpi()
+        abs_size = max(h_list)/dpi
+
+        return rel_size, abs_size
+
+
+class Fraction(_Base):
+    """
+    An instance whose size is a *fraction* of the *ref_size*.
+    ::
+
+      >>> s = Fraction(0.3, AxesX(ax))
+
+    """
+    def __init__(self, fraction, ref_size):
+        self._fraction_ref = ref_size
+        self._fraction = fraction
+
+    def get_size(self, renderer):
+        if self._fraction_ref is None:
+            return self._fraction, 0.
+        else:
+            r, a = self._fraction_ref.get_size(renderer)
+            rel_size = r*self._fraction
+            abs_size = a*self._fraction
+            return rel_size, abs_size
+
+class Padded(_Base):
+    """
+    Return a instance where the absolute part of *size* is
+    increase by the amount of *pad*.
+    """
+    def __init__(self, size, pad):
+        self._size = size
+        self._pad = pad
+
+    def get_size(self, renderer):
+        r, a = self._size.get_size(renderer)
+        rel_size = r
+        abs_size = a + self._pad
+        return rel_size, abs_size
+
+def from_any(size, fraction_ref=None):
+    """
+    Creates Fixed unit when the first argument is a float, or a
+    Fraction unit if that is a string that ends with %. The second
+    argument is only meaningful when Fraction unit is created.::
+
+      >>> a = Size.from_any(1.2) # => Size.Fixed(1.2)
+      >>> Size.from_any("50%", a) # => Size.Fraction(0.5, a)
+
+    """
+    if cbook.is_numlike(size):
+        return Fixed(size)
+    elif isinstance(size, six.string_types):
+        if size[-1] == "%":
+            return Fraction(float(size[:-1]) / 100, fraction_ref)
+
+    raise ValueError("Unknown format")
+
+
+class SizeFromFunc(_Base):
+    def __init__(self, func):
+        self._func = func
+
+    def get_size(self, renderer):
+        rel_size = 0.
+
+        bb = self._func(renderer)
+        dpi = renderer.points_to_pixels(72.)
+        abs_size = bb/dpi
+
+        return rel_size, abs_size
+
+class GetExtentHelper(object):
+    def _get_left(tight_bbox, axes_bbox):
+        return axes_bbox.xmin - tight_bbox.xmin
+
+    def _get_right(tight_bbox, axes_bbox):
+        return tight_bbox.xmax - axes_bbox.xmax
+
+    def _get_bottom(tight_bbox, axes_bbox):
+        return axes_bbox.ymin - tight_bbox.ymin
+
+    def _get_top(tight_bbox, axes_bbox):
+        return tight_bbox.ymax - axes_bbox.ymax
+
+    _get_func_map = dict(left=_get_left,
+                         right=_get_right,
+                         bottom=_get_bottom,
+                         top=_get_top)
+
+    del _get_left, _get_right, _get_bottom, _get_top
+
+    def __init__(self, ax, direction):
+        if isinstance(ax, Axes):
+            self._ax_list = [ax]
+        else:
+            self._ax_list = ax
+
+        try:
+            self._get_func = self._get_func_map[direction]
+        except KeyError:
+            raise KeyError("direction must be one of left, right, bottom, top")
+
+    def __call__(self, renderer):
+        vl = [self._get_func(ax.get_tightbbox(renderer, False),
+                             ax.bbox) for ax in self._ax_list]
+        return max(vl)
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/colorbar.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/colorbar.py
new file mode 100644
index 0000000000..34bdf3618a
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/colorbar.py
@@ -0,0 +1,836 @@
+"""
+Colorbar toolkit with two classes and a function:
+
+    :class:`ColorbarBase`
+        the base class with full colorbar drawing functionality.
+        It can be used as-is to make a colorbar for a given colormap;
+        a mappable object (e.g., image) is not needed.
+
+    :class:`Colorbar`
+        the derived class for use with images or contour plots.
+
+    :func:`make_axes`
+        a function for resizing an axes and adding a second axes
+        suitable for a colorbar
+
+The :meth:`~matplotlib.figure.Figure.colorbar` method uses :func:`make_axes`
+and :class:`Colorbar`; the :func:`~matplotlib.pyplot.colorbar` function
+is a thin wrapper over :meth:`~matplotlib.figure.Figure.colorbar`.
+"""
+
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import six
+from six.moves import xrange, zip
+
+import numpy as np
+import matplotlib as mpl
+import matplotlib.colors as colors
+import matplotlib.cm as cm
+from matplotlib import docstring
+import matplotlib.ticker as ticker
+import matplotlib.cbook as cbook
+import matplotlib.collections as collections
+import matplotlib.contour as contour
+from matplotlib.path import Path
+from matplotlib.patches import PathPatch
+from matplotlib.transforms import Bbox
+
+
+make_axes_kw_doc = '''
+
+    ============= ====================================================
+    Property      Description
+    ============= ====================================================
+    *orientation* vertical or horizontal
+    *fraction*    0.15; fraction of original axes to use for colorbar
+    *pad*         0.05 if vertical, 0.15 if horizontal; fraction
+                  of original axes between colorbar and new image axes
+    *shrink*      1.0; fraction by which to shrink the colorbar
+    *aspect*      20; ratio of long to short dimensions
+    ============= ====================================================
+
+'''
+
+colormap_kw_doc = '''
+
+    ===========   ====================================================
+    Property      Description
+    ===========   ====================================================
+    *extend*      [ 'neither' | 'both' | 'min' | 'max' ]
+                  If not 'neither', make pointed end(s) for out-of-
+                  range values.  These are set for a given colormap
+                  using the colormap set_under and set_over methods.
+    *spacing*     [ 'uniform' | 'proportional' ]
+                  Uniform spacing gives each discrete color the same
+                  space; proportional makes the space proportional to
+                  the data interval.
+    *ticks*       [ None | list of ticks | Locator object ]
+                  If None, ticks are determined automatically from the
+                  input.
+    *format*      [ None | format string | Formatter object ]
+                  If None, the
+                  :class:`~matplotlib.ticker.ScalarFormatter` is used.
+                  If a format string is given, e.g., '%.3f', that is
+                  used. An alternative
+                  :class:`~matplotlib.ticker.Formatter` object may be
+                  given instead.
+    *drawedges*   bool
+                  Whether to draw lines at color boundaries.
+    ===========   ====================================================
+
+    The following will probably be useful only in the context of
+    indexed colors (that is, when the mappable has norm=NoNorm()),
+    or other unusual circumstances.
+
+    ============   ===================================================
+    Property       Description
+    ============   ===================================================
+    *boundaries*   None or a sequence
+    *values*       None or a sequence which must be of length 1 less
+                   than the sequence of *boundaries*. For each region
+                   delimited by adjacent entries in *boundaries*, the
+                   color mapped to the corresponding value in values
+                   will be used.
+    ============   ===================================================
+
+'''
+
+colorbar_doc = '''
+
+Add a colorbar to a plot.
+
+Function signatures for the :mod:`~matplotlib.pyplot` interface; all
+but the first are also method signatures for the
+:meth:`~matplotlib.figure.Figure.colorbar` method::
+
+  colorbar(**kwargs)
+  colorbar(mappable, **kwargs)
+  colorbar(mappable, cax=cax, **kwargs)
+  colorbar(mappable, ax=ax, **kwargs)
+
+arguments:
+
+  *mappable*
+    the :class:`~matplotlib.image.Image`,
+    :class:`~matplotlib.contour.ContourSet`, etc. to
+    which the colorbar applies; this argument is mandatory for the
+    :meth:`~matplotlib.figure.Figure.colorbar` method but optional for the
+    :func:`~matplotlib.pyplot.colorbar` function, which sets the
+    default to the current image.
+
+keyword arguments:
+
+  *cax*
+    None | axes object into which the colorbar will be drawn
+  *ax*
+    None | parent axes object from which space for a new
+    colorbar axes will be stolen
+
+
+Additional keyword arguments are of two kinds:
+
+  axes properties:
+    %s
+  colorbar properties:
+    %s
+
+If *mappable* is a :class:`~matplotlib.contours.ContourSet`, its *extend*
+kwarg is included automatically.
+
+Note that the *shrink* kwarg provides a simple way to keep a vertical
+colorbar, for example, from being taller than the axes of the mappable
+to which the colorbar is attached; but it is a manual method requiring
+some trial and error. If the colorbar is too tall (or a horizontal
+colorbar is too wide) use a smaller value of *shrink*.
+
+For more precise control, you can manually specify the positions of
+the axes objects in which the mappable and the colorbar are drawn.  In
+this case, do not use any of the axes properties kwargs.
+
+It is known that some vector graphics viewer (svg and pdf) renders white gaps
+between segments of the colorbar. This is due to bugs in the viewers not
+matplotlib. As a workaround the colorbar can be rendered with overlapping
+segments::
+
+    cbar = colorbar()
+    cbar.solids.set_edgecolor("face")
+    draw()
+
+However this has negative consequences in other circumstances. Particularly with
+semi transparent images (alpha < 1) and colorbar extensions and is not enabled
+by default see (issue #1188).
+
+returns:
+    :class:`~matplotlib.colorbar.Colorbar` instance; see also its base class,
+    :class:`~matplotlib.colorbar.ColorbarBase`.  Call the
+    :meth:`~matplotlib.colorbar.ColorbarBase.set_label` method
+    to label the colorbar.
+
+
+The transData of the *cax* is adjusted so that the limits in the
+longest axis actually corresponds to the limits in colorbar range. On
+the other hand, the shortest axis has a data limits of [1,2], whose
+unconventional value is to prevent underflow when log scale is used.
+''' % (make_axes_kw_doc, colormap_kw_doc)
+
+#docstring.interpd.update(colorbar_doc=colorbar_doc)
+
+
+class CbarAxesLocator(object):
+    """
+    CbarAxesLocator is a axes_locator for colorbar axes. It adjust the
+    position of the axes to make a room for extended ends, i.e., the
+    extended ends are located outside the axes area.
+    """
+
+    def __init__(self, locator=None, extend="neither", orientation="vertical"):
+        """
+        *locator* : the bbox returned from the locator is used as a
+            initial axes location. If None, axes.bbox is used.
+
+        *extend* : same as in ColorbarBase
+        *orientation* : same as in ColorbarBase
+
+        """
+        self._locator = locator
+        self.extesion_fraction = 0.05
+        self.extend = extend
+        self.orientation = orientation
+
+    def get_original_position(self, axes, renderer):
+        """
+        get the original position of the axes.
+        """
+        if self._locator is None:
+            bbox = axes.get_position(original=True)
+        else:
+            bbox = self._locator(axes, renderer)
+        return bbox
+
+    def get_end_vertices(self):
+        """
+        return a tuple of two vertices for the colorbar extended ends.
+        The first vertices is for the minimum end, and the second is for
+        the maximum end.
+        """
+        # Note that concatenating two vertices needs to make a
+        # vertices for the frame.
+        extesion_fraction = self.extesion_fraction
+
+        corx = extesion_fraction*2.
+        cory = 1./(1. - corx)
+        x1, y1, w, h = 0, 0, 1, 1
+        x2, y2 = x1 + w, y1 + h
+        dw, dh = w*extesion_fraction, h*extesion_fraction*cory
+
+        if self.extend in ["min", "both"]:
+            bottom = [(x1, y1),
+                      (x1+w/2., y1-dh),
+                      (x2, y1)]
+        else:
+            bottom = [(x1, y1),
+                      (x2, y1)]
+
+        if self.extend in ["max", "both"]:
+            top = [(x2, y2),
+                   (x1+w/2., y2+dh),
+                   (x1, y2)]
+        else:
+            top = [(x2, y2),
+                   (x1, y2)]
+
+        if self.orientation == "horizontal":
+            bottom = [(y,x) for (x,y) in bottom]
+            top = [(y,x) for (x,y) in top]
+
+        return bottom, top
+
+
+    def get_path_patch(self):
+        """
+        get the path for axes patch
+        """
+        end1, end2 = self.get_end_vertices()
+
+        verts = [] + end1 + end2 + end1[:1]
+
+        return Path(verts)
+
+
+    def get_path_ends(self):
+        """
+        get the paths for extended ends
+        """
+
+        end1, end2 = self.get_end_vertices()
+
+        return Path(end1), Path(end2)
+
+
+    def __call__(self, axes, renderer):
+        """
+        Return the adjusted position of the axes
+        """
+        bbox0 = self.get_original_position(axes, renderer)
+        bbox = bbox0
+
+        x1, y1, w, h = bbox.bounds
+        extesion_fraction = self.extesion_fraction
+        dw, dh = w*extesion_fraction, h*extesion_fraction
+
+        if self.extend in ["min", "both"]:
+            if self.orientation == "horizontal":
+                x1 = x1 + dw
+            else:
+                y1 = y1+dh
+
+        if self.extend in ["max", "both"]:
+            if self.orientation == "horizontal":
+                w = w-2*dw
+            else:
+                h = h-2*dh
+
+        return Bbox.from_bounds(x1, y1, w, h)
+
+
+
+class ColorbarBase(cm.ScalarMappable):
+    '''
+    Draw a colorbar in an existing axes.
+
+    This is a base class for the :class:`Colorbar` class, which is the
+    basis for the :func:`~matplotlib.pyplot.colorbar` method and pylab
+    function.
+
+    It is also useful by itself for showing a colormap.  If the *cmap*
+    kwarg is given but *boundaries* and *values* are left as None,
+    then the colormap will be displayed on a 0-1 scale. To show the
+    under- and over-value colors, specify the *norm* as::
+
+        colors.Normalize(clip=False)
+
+    To show the colors versus index instead of on the 0-1 scale,
+    use::
+
+        norm=colors.NoNorm.
+
+    Useful attributes:
+
+        :attr:`ax`
+            the Axes instance in which the colorbar is drawn
+
+        :attr:`lines`
+            a LineCollection if lines were drawn, otherwise None
+
+        :attr:`dividers`
+            a LineCollection if *drawedges* is True, otherwise None
+
+    Useful public methods are :meth:`set_label` and :meth:`add_lines`.
+
+    '''
+
+    def __init__(self, ax, cmap=None,
+                           norm=None,
+                           alpha=1.0,
+                           values=None,
+                           boundaries=None,
+                           orientation='vertical',
+                           extend='neither',
+                           spacing='uniform',  # uniform or proportional
+                           ticks=None,
+                           format=None,
+                           drawedges=False,
+                           filled=True,
+                           ):
+        self.ax = ax
+
+        if cmap is None: cmap = cm.get_cmap()
+        if norm is None: norm = colors.Normalize()
+        self.alpha = alpha
+        cm.ScalarMappable.__init__(self, cmap=cmap, norm=norm)
+        self.values = values
+        self.boundaries = boundaries
+        self.extend = extend
+        self.spacing = spacing
+        self.orientation = orientation
+        self.drawedges = drawedges
+        self.filled = filled
+
+        # artists
+        self.solids = None
+        self.lines = None
+        self.dividers = None
+        self.extension_patch1 = None
+        self.extension_patch2 = None
+
+        if orientation == "vertical":
+            self.cbar_axis = self.ax.yaxis
+        else:
+            self.cbar_axis = self.ax.xaxis
+
+
+        if format is None:
+            if isinstance(self.norm, colors.LogNorm):
+                # change both axis for proper aspect
+                self.ax.set_xscale("log")
+                self.ax.set_yscale("log")
+                self.cbar_axis.set_minor_locator(ticker.NullLocator())
+                formatter = ticker.LogFormatter()
+            else:
+                formatter = None
+        elif isinstance(format, six.string_types):
+            formatter = ticker.FormatStrFormatter(format)
+        else:
+            formatter = format  # Assume it is a Formatter
+
+        if formatter is None:
+            formatter = self.cbar_axis.get_major_formatter()
+        else:
+            self.cbar_axis.set_major_formatter(formatter)
+
+        if cbook.iterable(ticks):
+            self.cbar_axis.set_ticks(ticks)
+        elif ticks is not None:
+            self.cbar_axis.set_major_locator(ticks)
+        else:
+            self._select_locator(formatter)
+
+
+        self._config_axes()
+
+        self.update_artists()
+
+        self.set_label_text('')
+
+
+    def _get_colorbar_limits(self):
+        """
+        initial limits for colorbar range. The returned min, max values
+        will be used to create colorbar solid(?) and etc.
+        """
+        if self.boundaries is not None:
+            C = self.boundaries
+            if self.extend in ["min", "both"]:
+                C = C[1:]
+
+            if self.extend in ["max", "both"]:
+                C = C[:-1]
+            return min(C), max(C)
+        else:
+            return self.get_clim()
+
+
+    def _config_axes(self):
+        '''
+        Adjust the properties of the axes to be adequate for colorbar display.
+        '''
+        ax = self.ax
+
+        axes_locator = CbarAxesLocator(ax.get_axes_locator(),
+                                       extend=self.extend,
+                                       orientation=self.orientation)
+        ax.set_axes_locator(axes_locator)
+
+        # override the get_data_ratio for the aspect works.
+        def _f():
+            return 1.
+        ax.get_data_ratio = _f
+        ax.get_data_ratio_log = _f
+
+        ax.set_frame_on(True)
+        ax.set_navigate(False)
+
+        self.ax.set_autoscalex_on(False)
+        self.ax.set_autoscaley_on(False)
+
+        if self.orientation == 'horizontal':
+            ax.xaxis.set_label_position('bottom')
+            ax.set_yticks([])
+        else:
+            ax.set_xticks([])
+            ax.yaxis.set_label_position('right')
+            ax.yaxis.set_ticks_position('right')
+
+
+
+    def update_artists(self):
+        """
+        Update the colorbar associated artists, *filled* and
+        *ends*. Note that *lines* are not updated.  This needs to be
+        called whenever clim of associated image changes.
+        """
+        self._process_values()
+        self._add_ends()
+
+        X, Y = self._mesh()
+        if self.filled:
+            C = self._values[:,np.newaxis]
+            self._add_solids(X, Y, C)
+
+        ax = self.ax
+        vmin, vmax = self._get_colorbar_limits()
+        if self.orientation == 'horizontal':
+            ax.set_ylim(1, 2)
+            ax.set_xlim(vmin, vmax)
+        else:
+            ax.set_xlim(1, 2)
+            ax.set_ylim(vmin, vmax)
+
+
+    def _add_ends(self):
+        """
+        Create patches from extended ends and add them to the axes.
+        """
+
+        del self.extension_patch1
+        del self.extension_patch2
+
+        path1, path2 = self.ax.get_axes_locator().get_path_ends()
+        fc=mpl.rcParams['axes.facecolor']
+        ec=mpl.rcParams['axes.edgecolor']
+        linewidths=0.5*mpl.rcParams['axes.linewidth']
+        self.extension_patch1 = PathPatch(path1,
+                                          fc=fc, ec=ec, lw=linewidths,
+                                          zorder=2.,
+                                          transform=self.ax.transAxes,
+                                          clip_on=False)
+        self.extension_patch2 = PathPatch(path2,
+                                          fc=fc, ec=ec, lw=linewidths,
+                                          zorder=2.,
+                                          transform=self.ax.transAxes,
+                                          clip_on=False)
+        self.ax.add_artist(self.extension_patch1)
+        self.ax.add_artist(self.extension_patch2)
+
+
+
+    def _set_label_text(self):
+        """
+        set label.
+        """
+        self.cbar_axis.set_label_text(self._label, **self._labelkw)
+
+    def set_label_text(self, label, **kw):
+        '''
+        Label the long axis of the colorbar
+        '''
+        self._label = label
+        self._labelkw = kw
+        self._set_label_text()
+
+
+    def _edges(self, X, Y):
+        '''
+        Return the separator line segments; helper for _add_solids.
+        '''
+        N = X.shape[0]
+        # Using the non-array form of these line segments is much
+        # simpler than making them into arrays.
+        if self.orientation == 'vertical':
+            return [list(zip(X[i], Y[i])) for i in xrange(1, N-1)]
+        else:
+            return [list(zip(Y[i], X[i])) for i in xrange(1, N-1)]
+
+    def _add_solids(self, X, Y, C):
+        '''
+        Draw the colors using :meth:`~matplotlib.axes.Axes.pcolormesh`;
+        optionally add separators.
+        '''
+        ## Change to pcolorfast after fixing bugs in some backends...
+
+        if self.extend in ["min", "both"]:
+            cc = self.to_rgba([C[0][0]])
+            self.extension_patch1.set_fc(cc[0])
+            X, Y, C = X[1:], Y[1:], C[1:]
+
+        if self.extend in ["max", "both"]:
+            cc = self.to_rgba([C[-1][0]])
+            self.extension_patch2.set_fc(cc[0])
+            X, Y, C = X[:-1], Y[:-1], C[:-1]
+
+        if self.orientation == 'vertical':
+            args = (X, Y, C)
+        else:
+            args = (np.transpose(Y), np.transpose(X), np.transpose(C))
+        kw = {'cmap':self.cmap, 'norm':self.norm,
+              'shading':'flat', 'alpha':self.alpha,
+              }
+
+        del self.solids
+        del self.dividers
+
+        col = self.ax.pcolormesh(*args, **kw)
+
+        self.solids = col
+        if self.drawedges:
+            self.dividers = collections.LineCollection(self._edges(X,Y),
+                              colors=(mpl.rcParams['axes.edgecolor'],),
+                              linewidths=(0.5*mpl.rcParams['axes.linewidth'],),
+                              )
+            self.ax.add_collection(self.dividers)
+        else:
+            self.dividers = None
+
+    def add_lines(self, levels, colors, linewidths):
+        '''
+        Draw lines on the colorbar. It deletes preexisting lines.
+        '''
+        del self.lines
+
+        N = len(levels)
+        x = np.array([1.0, 2.0])
+        X, Y = np.meshgrid(x,levels)
+        if self.orientation == 'vertical':
+            xy = [list(zip(X[i], Y[i])) for i in xrange(N)]
+        else:
+            xy = [list(zip(Y[i], X[i])) for i in xrange(N)]
+        col = collections.LineCollection(xy, linewidths=linewidths,
+                                         )
+        self.lines = col
+        col.set_color(colors)
+        self.ax.add_collection(col)
+
+
+    def _select_locator(self, formatter):
+        '''
+        select a suitable locator
+        '''
+        if self.boundaries is None:
+            if isinstance(self.norm, colors.NoNorm):
+                nv = len(self._values)
+                base = 1 + int(nv/10)
+                locator = ticker.IndexLocator(base=base, offset=0)
+            elif isinstance(self.norm, colors.BoundaryNorm):
+                b = self.norm.boundaries
+                locator = ticker.FixedLocator(b, nbins=10)
+            elif isinstance(self.norm, colors.LogNorm):
+                locator = ticker.LogLocator()
+            else:
+                locator = ticker.MaxNLocator(nbins=5)
+        else:
+            b = self._boundaries[self._inside]
+            locator = ticker.FixedLocator(b) #, nbins=10)
+
+        self.cbar_axis.set_major_locator(locator)
+
+
+    def _process_values(self, b=None):
+        '''
+        Set the :attr:`_boundaries` and :attr:`_values` attributes
+        based on the input boundaries and values.  Input boundaries
+        can be *self.boundaries* or the argument *b*.
+        '''
+        if b is None:
+            b = self.boundaries
+        if b is not None:
+            self._boundaries = np.asarray(b, dtype=float)
+            if self.values is None:
+                self._values = 0.5*(self._boundaries[:-1]
+                                        + self._boundaries[1:])
+                if isinstance(self.norm, colors.NoNorm):
+                    self._values = (self._values + 0.00001).astype(np.int16)
+                return
+            self._values = np.array(self.values)
+            return
+        if self.values is not None:
+            self._values = np.array(self.values)
+            if self.boundaries is None:
+                b = np.zeros(len(self.values)+1, 'd')
+                b[1:-1] = 0.5*(self._values[:-1] - self._values[1:])
+                b[0] = 2.0*b[1] - b[2]
+                b[-1] = 2.0*b[-2] - b[-3]
+                self._boundaries = b
+                return
+            self._boundaries = np.array(self.boundaries)
+            return
+        # Neither boundaries nor values are specified;
+        # make reasonable ones based on cmap and norm.
+        if isinstance(self.norm, colors.NoNorm):
+            b = self._uniform_y(self.cmap.N+1) * self.cmap.N - 0.5
+            v = np.zeros((len(b)-1,), dtype=np.int16)
+            v = np.arange(self.cmap.N, dtype=np.int16)
+            self._boundaries = b
+            self._values = v
+            return
+        elif isinstance(self.norm, colors.BoundaryNorm):
+            b = np.array(self.norm.boundaries)
+            v = np.zeros((len(b)-1,), dtype=float)
+            bi = self.norm.boundaries
+            v = 0.5*(bi[:-1] + bi[1:])
+            self._boundaries = b
+            self._values = v
+            return
+        else:
+            b = self._uniform_y(self.cmap.N+1)
+
+        self._process_values(b)
+
+
+    def _uniform_y(self, N):
+        '''
+        Return colorbar data coordinates for *N* uniformly
+        spaced boundaries.
+        '''
+        vmin, vmax = self._get_colorbar_limits()
+        if isinstance(self.norm, colors.LogNorm):
+            y = np.logspace(np.log10(vmin), np.log10(vmax), N)
+        else:
+            y = np.linspace(vmin, vmax, N)
+        return y
+
+    def _mesh(self):
+        '''
+        Return X,Y, the coordinate arrays for the colorbar pcolormesh.
+        These are suitable for a vertical colorbar; swapping and
+        transposition for a horizontal colorbar are done outside
+        this function.
+        '''
+        x = np.array([1.0, 2.0])
+        if self.spacing == 'uniform':
+            y = self._uniform_y(len(self._boundaries))
+        else:
+            y = self._boundaries
+        self._y = y
+
+        X, Y = np.meshgrid(x,y)
+        return X, Y
+
+
+    def set_alpha(self, alpha):
+        """
+        set alpha value.
+        """
+        self.alpha = alpha
+
+
+class Colorbar(ColorbarBase):
+    def __init__(self, ax, mappable, **kw):
+        mappable.autoscale_None() # Ensure mappable.norm.vmin, vmax
+                             # are set when colorbar is called,
+                             # even if mappable.draw has not yet
+                             # been called.  This will not change
+                             # vmin, vmax if they are already set.
+        self.mappable = mappable
+        kw['cmap'] = mappable.cmap
+        kw['norm'] = mappable.norm
+        kw['alpha'] = mappable.get_alpha()
+        if isinstance(mappable, contour.ContourSet):
+            CS = mappable
+            kw['boundaries'] = CS._levels
+            kw['values'] = CS.cvalues
+            kw['extend'] = CS.extend
+            #kw['ticks'] = CS._levels
+            kw.setdefault('ticks', ticker.FixedLocator(CS.levels, nbins=10))
+            kw['filled'] = CS.filled
+            ColorbarBase.__init__(self, ax, **kw)
+            if not CS.filled:
+                self.add_lines(CS)
+        else:
+            ColorbarBase.__init__(self, ax, **kw)
+
+
+    def add_lines(self, CS):
+        '''
+        Add the lines from a non-filled
+        :class:`~matplotlib.contour.ContourSet` to the colorbar.
+        '''
+        if not isinstance(CS, contour.ContourSet) or CS.filled:
+            raise ValueError('add_lines is only for a ContourSet of lines')
+        tcolors = [c[0] for c in CS.tcolors]
+        tlinewidths = [t[0] for t in CS.tlinewidths]
+        # The following was an attempt to get the colorbar lines
+        # to follow subsequent changes in the contour lines,
+        # but more work is needed: specifically, a careful
+        # look at event sequences, and at how
+        # to make one object track another automatically.
+        #tcolors = [col.get_colors()[0] for col in CS.collections]
+        #tlinewidths = [col.get_linewidth()[0] for lw in CS.collections]
+        ColorbarBase.add_lines(self, CS.levels, tcolors, tlinewidths)
+
+    def update_bruteforce(self, mappable):
+        """
+        Update the colorbar artists to reflect the change of the
+        associated mappable.
+        """
+        self.update_artists()
+
+        if isinstance(mappable, contour.ContourSet):
+            if not mappable.filled:
+                self.add_lines(mappable)
+
+@docstring.Substitution(make_axes_kw_doc)
+def make_axes(parent, **kw):
+    '''
+    Resize and reposition a parent axes, and return a child
+    axes suitable for a colorbar
+
+    ::
+
+        cax, kw = make_axes(parent, **kw)
+
+    Keyword arguments may include the following (with defaults):
+
+        *orientation*
+            'vertical'  or 'horizontal'
+
+    %s
+
+    All but the first of these are stripped from the input kw set.
+
+    Returns (cax, kw), the child axes and the reduced kw dictionary.
+    '''
+    orientation = kw.setdefault('orientation', 'vertical')
+    fraction = kw.pop('fraction', 0.15)
+    shrink = kw.pop('shrink', 1.0)
+    aspect = kw.pop('aspect', 20)
+    #pb = transforms.PBox(parent.get_position())
+    pb = parent.get_position(original=True).frozen()
+    if orientation == 'vertical':
+        pad = kw.pop('pad', 0.05)
+        x1 = 1.0-fraction
+        pb1, pbx, pbcb = pb.splitx(x1-pad, x1)
+        pbcb = pbcb.shrunk(1.0, shrink).anchored('C', pbcb)
+        anchor = (0.0, 0.5)
+        panchor = (1.0, 0.5)
+    else:
+        pad = kw.pop('pad', 0.15)
+        pbcb, pbx, pb1 = pb.splity(fraction, fraction+pad)
+        pbcb = pbcb.shrunk(shrink, 1.0).anchored('C', pbcb)
+        aspect = 1.0/aspect
+        anchor = (0.5, 1.0)
+        panchor = (0.5, 0.0)
+    parent.set_position(pb1)
+    parent.set_anchor(panchor)
+    fig = parent.get_figure()
+    cax = fig.add_axes(pbcb)
+    cax.set_aspect(aspect, anchor=anchor, adjustable='box')
+    return cax, kw
+
+@docstring.Substitution(colorbar_doc)
+def colorbar(mappable, cax=None, ax=None, **kw):
+    """
+    Create a colorbar for a ScalarMappable instance.
+
+    Documentation for the pylab thin wrapper:
+
+    %s
+    """
+    import matplotlib.pyplot as plt
+    if ax is None:
+        ax = plt.gca()
+    if cax is None:
+        cax, kw = make_axes(ax, **kw)
+    cax._hold = True
+    cb = Colorbar(cax, mappable, **kw)
+
+    def on_changed(m):
+        cb.set_cmap(m.get_cmap())
+        cb.set_clim(m.get_clim())
+        cb.update_bruteforce(m)
+
+    cbid = mappable.callbacksSM.connect('changed', on_changed)
+    mappable.colorbar = cb
+    ax.figure.sca(ax)
+    return cb
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/inset_locator.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/inset_locator.py
new file mode 100644
index 0000000000..9aeedcb088
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/inset_locator.py
@@ -0,0 +1,659 @@
+"""
+A collection of functions and objects for creating or placing inset axes.
+"""
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import warnings
+from matplotlib import docstring
+import six
+from matplotlib.offsetbox import AnchoredOffsetbox
+from matplotlib.patches import Patch, Rectangle
+from matplotlib.path import Path
+from matplotlib.transforms import Bbox, BboxTransformTo
+from matplotlib.transforms import IdentityTransform, TransformedBbox
+
+from . import axes_size as Size
+from .parasite_axes import HostAxes
+
+
+class InsetPosition(object):
+    @docstring.dedent_interpd
+    def __init__(self, parent, lbwh):
+        """
+        An object for positioning an inset axes.
+
+        This is created by specifying the normalized coordinates in the axes,
+        instead of the figure.
+
+        Parameters
+        ----------
+        parent : `matplotlib.axes.Axes`
+            Axes to use for normalizing coordinates.
+
+        lbwh : iterable of four floats
+            The left edge, bottom edge, width, and height of the inset axes, in
+            units of the normalized coordinate of the *parent* axes.
+
+        See Also
+        --------
+        :meth:`matplotlib.axes.Axes.set_axes_locator`
+
+        Examples
+        --------
+        The following bounds the inset axes to a box with 20%% of the parent
+        axes's height and 40%% of the width. The size of the axes specified
+        ([0, 0, 1, 1]) ensures that the axes completely fills the bounding box:
+
+        >>> parent_axes = plt.gca()
+        >>> ax_ins = plt.axes([0, 0, 1, 1])
+        >>> ip = InsetPosition(ax, [0.5, 0.1, 0.4, 0.2])
+        >>> ax_ins.set_axes_locator(ip)
+        """
+        self.parent = parent
+        self.lbwh = lbwh
+
+    def __call__(self, ax, renderer):
+        bbox_parent = self.parent.get_position(original=False)
+        trans = BboxTransformTo(bbox_parent)
+        bbox_inset = Bbox.from_bounds(*self.lbwh)
+        bb = TransformedBbox(bbox_inset, trans)
+        return bb
+
+
+class AnchoredLocatorBase(AnchoredOffsetbox):
+    def __init__(self, bbox_to_anchor, offsetbox, loc,
+                 borderpad=0.5, bbox_transform=None):
+        super(AnchoredLocatorBase, self).__init__(
+            loc, pad=0., child=None, borderpad=borderpad,
+            bbox_to_anchor=bbox_to_anchor, bbox_transform=bbox_transform
+        )
+
+    def draw(self, renderer):
+        raise RuntimeError("No draw method should be called")
+
+    def __call__(self, ax, renderer):
+        self.axes = ax
+
+        fontsize = renderer.points_to_pixels(self.prop.get_size_in_points())
+        self._update_offset_func(renderer, fontsize)
+
+        width, height, xdescent, ydescent = self.get_extent(renderer)
+
+        px, py = self.get_offset(width, height, 0, 0, renderer)
+        bbox_canvas = Bbox.from_bounds(px, py, width, height)
+        tr = ax.figure.transFigure.inverted()
+        bb = TransformedBbox(bbox_canvas, tr)
+
+        return bb
+
+
+class AnchoredSizeLocator(AnchoredLocatorBase):
+    def __init__(self, bbox_to_anchor, x_size, y_size, loc,
+                 borderpad=0.5, bbox_transform=None):
+
+        super(AnchoredSizeLocator, self).__init__(
+            bbox_to_anchor, None, loc,
+            borderpad=borderpad, bbox_transform=bbox_transform
+        )
+
+        self.x_size = Size.from_any(x_size)
+        self.y_size = Size.from_any(y_size)
+
+    def get_extent(self, renderer):
+        x, y, w, h = self.get_bbox_to_anchor().bounds
+
+        dpi = renderer.points_to_pixels(72.)
+
+        r, a = self.x_size.get_size(renderer)
+        width = w * r + a * dpi
+
+        r, a = self.y_size.get_size(renderer)
+        height = h * r + a * dpi
+        xd, yd = 0, 0
+
+        fontsize = renderer.points_to_pixels(self.prop.get_size_in_points())
+        pad = self.pad * fontsize
+
+        return width + 2 * pad, height + 2 * pad, xd + pad, yd + pad
+
+
+class AnchoredZoomLocator(AnchoredLocatorBase):
+    def __init__(self, parent_axes, zoom, loc,
+                 borderpad=0.5,
+                 bbox_to_anchor=None,
+                 bbox_transform=None):
+        self.parent_axes = parent_axes
+        self.zoom = zoom
+
+        if bbox_to_anchor is None:
+            bbox_to_anchor = parent_axes.bbox
+
+        super(AnchoredZoomLocator, self).__init__(
+            bbox_to_anchor, None, loc, borderpad=borderpad,
+            bbox_transform=bbox_transform)
+
+    def get_extent(self, renderer):
+        bb = TransformedBbox(self.axes.viewLim,
+                             self.parent_axes.transData)
+
+        x, y, w, h = bb.bounds
+        fontsize = renderer.points_to_pixels(self.prop.get_size_in_points())
+        pad = self.pad * fontsize
+
+        return abs(w * self.zoom) + 2 * pad, abs(h * self.zoom) + 2 * pad, pad, pad
+
+
+class BboxPatch(Patch):
+    @docstring.dedent_interpd
+    def __init__(self, bbox, **kwargs):
+        """
+        Patch showing the shape bounded by a Bbox.
+
+        Parameters
+        ----------
+        bbox : `matplotlib.transforms.Bbox`
+            Bbox to use for the extents of this patch.
+
+        **kwargs
+            Patch properties. Valid arguments include:
+            %(Patch)s
+        """
+        if "transform" in kwargs:
+            raise ValueError("transform should not be set")
+
+        kwargs["transform"] = IdentityTransform()
+        Patch.__init__(self, **kwargs)
+        self.bbox = bbox
+
+    def get_path(self):
+        x0, y0, x1, y1 = self.bbox.extents
+
+        verts = [(x0, y0),
+                 (x1, y0),
+                 (x1, y1),
+                 (x0, y1),
+                 (x0, y0),
+                 (0, 0)]
+
+        codes = [Path.MOVETO,
+                 Path.LINETO,
+                 Path.LINETO,
+                 Path.LINETO,
+                 Path.LINETO,
+                 Path.CLOSEPOLY]
+
+        return Path(verts, codes)
+
+    get_path.__doc__ = Patch.get_path.__doc__
+
+
+class BboxConnector(Patch):
+    @staticmethod
+    def get_bbox_edge_pos(bbox, loc):
+        """
+        Helper function to obtain the location of a corner of a bbox
+
+        Parameters
+        ----------
+        bbox : `matplotlib.transforms.Bbox`
+
+        loc : {1, 2, 3, 4}
+            Corner of *bbox*. Valid values are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4
+
+        Returns
+        -------
+        x, y : float
+            Coordinates of the corner specified by *loc*.
+        """
+        x0, y0, x1, y1 = bbox.extents
+        if loc == 1:
+            return x1, y1
+        elif loc == 2:
+            return x0, y1
+        elif loc == 3:
+            return x0, y0
+        elif loc == 4:
+            return x1, y0
+
+    @staticmethod
+    def connect_bbox(bbox1, bbox2, loc1, loc2=None):
+        """
+        Helper function to obtain a Path from one bbox to another.
+
+        Parameters
+        ----------
+        bbox1, bbox2 : `matplotlib.transforms.Bbox`
+            Bounding boxes to connect.
+
+        loc1 : {1, 2, 3, 4}
+            Corner of *bbox1* to use. Valid values are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4
+
+        loc2 : {1, 2, 3, 4}, optional
+            Corner of *bbox2* to use. If None, defaults to *loc1*.
+            Valid values are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4
+
+        Returns
+        -------
+        path : `matplotlib.path.Path`
+            A line segment from the *loc1* corner of *bbox1* to the *loc2*
+            corner of *bbox2*.
+        """
+        if isinstance(bbox1, Rectangle):
+            transform = bbox1.get_transfrom()
+            bbox1 = Bbox.from_bounds(0, 0, 1, 1)
+            bbox1 = TransformedBbox(bbox1, transform)
+
+        if isinstance(bbox2, Rectangle):
+            transform = bbox2.get_transform()
+            bbox2 = Bbox.from_bounds(0, 0, 1, 1)
+            bbox2 = TransformedBbox(bbox2, transform)
+
+        if loc2 is None:
+            loc2 = loc1
+
+        x1, y1 = BboxConnector.get_bbox_edge_pos(bbox1, loc1)
+        x2, y2 = BboxConnector.get_bbox_edge_pos(bbox2, loc2)
+
+        verts = [[x1, y1], [x2, y2]]
+        codes = [Path.MOVETO, Path.LINETO]
+
+        return Path(verts, codes)
+
+    @docstring.dedent_interpd
+    def __init__(self, bbox1, bbox2, loc1, loc2=None, **kwargs):
+        """
+        Connect two bboxes with a straight line.
+
+        Parameters
+        ----------
+        bbox1, bbox2 : `matplotlib.transforms.Bbox`
+            Bounding boxes to connect.
+
+        loc1 : {1, 2, 3, 4}
+            Corner of *bbox1* to draw the line. Valid values are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4
+
+        loc2 : {1, 2, 3, 4}, optional
+            Corner of *bbox2* to draw the line. If None, defaults to *loc1*.
+            Valid values are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4
+
+        **kwargs
+            Patch properties for the line drawn. Valid arguments include:
+            %(Patch)s
+        """
+        if "transform" in kwargs:
+            raise ValueError("transform should not be set")
+
+        kwargs["transform"] = IdentityTransform()
+        Patch.__init__(self, fill=False, **kwargs)
+        self.bbox1 = bbox1
+        self.bbox2 = bbox2
+        self.loc1 = loc1
+        self.loc2 = loc2
+
+    def get_path(self):
+        return self.connect_bbox(self.bbox1, self.bbox2,
+                                 self.loc1, self.loc2)
+
+    get_path.__doc__ = Patch.get_path.__doc__
+
+
+class BboxConnectorPatch(BboxConnector):
+    @docstring.dedent_interpd
+    def __init__(self, bbox1, bbox2, loc1a, loc2a, loc1b, loc2b, **kwargs):
+        """
+        Connect two bboxes with a quadrilateral.
+
+        The quadrilateral is specified by two lines that start and end at corners
+        of the bboxes. The four sides of the quadrilateral are defined by the two
+        lines given, the line between the two corners specified in *bbox1* and the
+        line between the two corners specified in *bbox2*.
+
+        Parameters
+        ----------
+        bbox1, bbox2 : `matplotlib.transforms.Bbox`
+            Bounding boxes to connect.
+
+        loc1a, loc2a : {1, 2, 3, 4}
+            Corners of *bbox1* and *bbox2* to draw the first line.
+            Valid values are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4
+
+        loc1b, loc2b : {1, 2, 3, 4}
+            Corners of *bbox1* and *bbox2* to draw the second line.
+            Valid values are::
+
+                'upper right'  : 1,
+                'upper left'   : 2,
+                'lower left'   : 3,
+                'lower right'  : 4
+
+        **kwargs
+            Patch properties for the line drawn:
+            %(Patch)s
+        """
+        if "transform" in kwargs:
+            raise ValueError("transform should not be set")
+        BboxConnector.__init__(self, bbox1, bbox2, loc1a, loc2a, **kwargs)
+        self.loc1b = loc1b
+        self.loc2b = loc2b
+
+    def get_path(self):
+        path1 = self.connect_bbox(self.bbox1, self.bbox2, self.loc1, self.loc2)
+        path2 = self.connect_bbox(self.bbox2, self.bbox1,
+                                  self.loc2b, self.loc1b)
+        path_merged = (list(path1.vertices) +
+                       list(path2.vertices) +
+                       [path1.vertices[0]])
+        return Path(path_merged)
+
+    get_path.__doc__ = BboxConnector.get_path.__doc__
+
+
+def _add_inset_axes(parent_axes, inset_axes):
+    """Helper function to add an inset axes and disable navigation in it"""
+    parent_axes.figure.add_axes(inset_axes)
+    inset_axes.set_navigate(False)
+
+
+@docstring.dedent_interpd
+def inset_axes(parent_axes, width, height, loc=1,
+               bbox_to_anchor=None, bbox_transform=None,
+               axes_class=None,
+               axes_kwargs=None,
+               borderpad=0.5):
+    """
+    Create an inset axes with a given width and height.
+
+    Both sizes used can be specified either in inches or percentage.
+    For example,::
+
+        inset_axes(parent_axes, width='40%%', height='30%%', loc=3)
+
+    creates in inset axes in the lower left corner of *parent_axes* which spans
+    over 30%% in height and 40%% in width of the *parent_axes*. Since the usage
+    of `.inset_axes` may become slightly tricky when exceeding such standard
+    cases, it is recommended to read
+    :ref:`the examples <sphx_glr_gallery_axes_grid1_inset_locator_demo.py>`.
+
+    Parameters
+    ----------
+    parent_axes : `matplotlib.axes.Axes`
+        Axes to place the inset axes.
+
+    width, height : float or str
+        Size of the inset axes to create. If a float is provided, it is
+        the size in inches, e.g. *width=1.3*. If a string is provided, it is
+        the size in relative units, e.g. *width='40%%'*. By default, i.e. if
+        neither *bbox_to_anchor* nor *bbox_transform* are specified, those
+        are relative to the parent_axes. Otherwise they are to be understood
+        relative to the bounding box provided via *bbox_to_anchor*.
+
+    loc : int or string, optional, default to 1
+        Location to place the inset axes. The valid locations are::
+
+            'upper right'  : 1,
+            'upper left'   : 2,
+            'lower left'   : 3,
+            'lower right'  : 4,
+            'right'        : 5,
+            'center left'  : 6,
+            'center right' : 7,
+            'lower center' : 8,
+            'upper center' : 9,
+            'center'       : 10
+
+    bbox_to_anchor : tuple or `matplotlib.transforms.BboxBase`, optional
+        Bbox that the inset axes will be anchored to. If None,
+        *parent_axes.bbox* is used. If a tuple, can be either
+        [left, bottom, width, height], or [left, bottom].
+        If the kwargs *width* and/or *height* are specified in relative units,
+        the 2-tuple [left, bottom] cannot be used. Note that
+        the units of the bounding box are determined through the transform
+        in use. When using *bbox_to_anchor* it almost always makes sense to
+        also specify a *bbox_transform*. This might often be the axes transform
+        *parent_axes.transAxes*.
+
+    bbox_transform : `matplotlib.transforms.Transform`, optional
+        Transformation for the bbox that contains the inset axes.
+        If None, a `.transforms.IdentityTransform` is used (i.e. pixel
+        coordinates). This is useful when not providing any argument to
+        *bbox_to_anchor*. When using *bbox_to_anchor* it almost always makes
+        sense to also specify a *bbox_transform*. This might often be the
+        axes transform *parent_axes.transAxes*. Inversely, when specifying
+        the axes- or figure-transform here, be aware that not specifying
+        *bbox_to_anchor* will use *parent_axes.bbox*, the units of which are
+        in display (pixel) coordinates.
+
+    axes_class : `matplotlib.axes.Axes` type, optional
+        If specified, the inset axes created will be created with this class's
+        constructor.
+
+    axes_kwargs : dict, optional
+        Keyworded arguments to pass to the constructor of the inset axes.
+        Valid arguments include:
+        %(Axes)s
+
+    borderpad : float, optional
+        Padding between inset axes and the bbox_to_anchor. Defaults to 0.5.
+        The units are axes font size, i.e. for a default font size of 10 points
+        *borderpad = 0.5* is equivalent to a padding of 5 points.
+
+    Returns
+    -------
+    inset_axes : `axes_class`
+        Inset axes object created.
+    """
+
+    if axes_class is None:
+        axes_class = HostAxes
+
+    if axes_kwargs is None:
+        inset_axes = axes_class(parent_axes.figure, parent_axes.get_position())
+    else:
+        inset_axes = axes_class(parent_axes.figure, parent_axes.get_position(),
+                                **axes_kwargs)
+
+    if bbox_transform in [parent_axes.transAxes,
+                          parent_axes.figure.transFigure]:
+        if bbox_to_anchor is None:
+            warnings.warn("Using the axes or figure transform requires a "
+                          "bounding box in the respective coordinates. "
+                          "Using bbox_to_anchor=(0,0,1,1) now.")
+            bbox_to_anchor = (0, 0, 1, 1)
+
+    if bbox_to_anchor is None:
+        bbox_to_anchor = parent_axes.bbox
+
+    if isinstance(bbox_to_anchor, tuple) and \
+        (isinstance(width, str) or isinstance(height, str)):
+        if len(bbox_to_anchor) != 4:
+            raise ValueError("Using relative units for width or height "
+                             "requires to provide a 4-tuple or a "
+                             "`BBox` instance to `bbox_to_anchor.")
+
+    axes_locator = AnchoredSizeLocator(bbox_to_anchor,
+                                       width, height,
+                                       loc=loc,
+                                       bbox_transform=bbox_transform,
+                                       borderpad=borderpad)
+
+    inset_axes.set_axes_locator(axes_locator)
+
+    _add_inset_axes(parent_axes, inset_axes)
+
+    return inset_axes
+
+
+@docstring.dedent_interpd
+def zoomed_inset_axes(parent_axes, zoom, loc=1,
+                      bbox_to_anchor=None, bbox_transform=None,
+                      axes_class=None,
+                      axes_kwargs=None,
+                      borderpad=0.5):
+    """
+    Create an anchored inset axes by scaling a parent axes. For usage, also see
+    :ref:`the examples <sphx_glr_gallery_axes_grid1_inset_locator_demo2.py>`.
+
+    Parameters
+    ----------
+    parent_axes : `matplotlib.axes.Axes`
+        Axes to place the inset axes.
+
+    zoom : float
+        Scaling factor of the data axes. *zoom* > 1 will enlargen the
+        coordinates (i.e., "zoomed in"), while *zoom* < 1 will shrink the
+        coordinates (i.e., "zoomed out").
+
+    loc : int or string, optional, default to 1
+        Location to place the inset axes. The valid locations are::
+
+            'upper right'  : 1,
+            'upper left'   : 2,
+            'lower left'   : 3,
+            'lower right'  : 4,
+            'right'        : 5,
+            'center left'  : 6,
+            'center right' : 7,
+            'lower center' : 8,
+            'upper center' : 9,
+            'center'       : 10
+
+    bbox_to_anchor : tuple or `matplotlib.transforms.BboxBase`, optional
+        Bbox that the inset axes will be anchored to. If None,
+        *parent_axes.bbox* is used. If a tuple, can be either
+        [left, bottom, width, height], or [left, bottom].
+        If the kwargs *width* and/or *height* are specified in relative units,
+        the 2-tuple [left, bottom] cannot be used. Note that
+        the units of the bounding box are determined through the transform
+        in use. When using *bbox_to_anchor* it almost always makes sense to
+        also specify a *bbox_transform*. This might often be the axes transform
+        *parent_axes.transAxes*.
+
+    bbox_transform : `matplotlib.transforms.Transform`, optional
+        Transformation for the bbox that contains the inset axes.
+        If None, a `.transforms.IdentityTransform` is used (i.e. pixel
+        coordinates). This is useful when not providing any argument to
+        *bbox_to_anchor*. When using *bbox_to_anchor* it almost always makes
+        sense to also specify a *bbox_transform*. This might often be the
+        axes transform *parent_axes.transAxes*. Inversely, when specifying
+        the axes- or figure-transform here, be aware that not specifying
+        *bbox_to_anchor* will use *parent_axes.bbox*, the units of which are
+        in display (pixel) coordinates.
+
+    axes_class : `matplotlib.axes.Axes` type, optional
+        If specified, the inset axes created will be created with this class's
+        constructor.
+
+    axes_kwargs : dict, optional
+        Keyworded arguments to pass to the constructor of the inset axes.
+        Valid arguments include:
+        %(Axes)s
+
+    borderpad : float, optional
+        Padding between inset axes and the bbox_to_anchor. Defaults to 0.5.
+        The units are axes font size, i.e. for a default font size of 10 points
+        *borderpad = 0.5* is equivalent to a padding of 5 points.
+
+    Returns
+    -------
+    inset_axes : `axes_class`
+        Inset axes object created.
+    """
+
+    if axes_class is None:
+        axes_class = HostAxes
+
+    if axes_kwargs is None:
+        inset_axes = axes_class(parent_axes.figure, parent_axes.get_position())
+    else:
+        inset_axes = axes_class(parent_axes.figure, parent_axes.get_position(),
+                                **axes_kwargs)
+
+    axes_locator = AnchoredZoomLocator(parent_axes, zoom=zoom, loc=loc,
+                                       bbox_to_anchor=bbox_to_anchor,
+                                       bbox_transform=bbox_transform,
+                                       borderpad=borderpad)
+    inset_axes.set_axes_locator(axes_locator)
+
+    _add_inset_axes(parent_axes, inset_axes)
+
+    return inset_axes
+
+
+@docstring.dedent_interpd
+def mark_inset(parent_axes, inset_axes, loc1, loc2, **kwargs):
+    """
+    Draw a box to mark the location of an area represented by an inset axes.
+
+    This function draws a box in *parent_axes* at the bounding box of
+    *inset_axes*, and shows a connection with the inset axes by drawing lines
+    at the corners, giving a "zoomed in" effect.
+
+    Parameters
+    ----------
+    parent_axes : `matplotlib.axes.Axes`
+        Axes which contains the area of the inset axes.
+
+    inset_axes : `matplotlib.axes.Axes`
+        The inset axes.
+
+    loc1, loc2 : {1, 2, 3, 4}
+        Corners to use for connecting the inset axes and the area in the
+        parent axes.
+
+    **kwargs
+        Patch properties for the lines and box drawn:
+        %(Patch)s
+
+    Returns
+    -------
+    pp : `matplotlib.patches.Patch`
+        The patch drawn to represent the area of the inset axes.
+
+    p1, p2 : `matplotlib.patches.Patch`
+        The patches connecting two corners of the inset axes and its area.
+    """
+    rect = TransformedBbox(inset_axes.viewLim, parent_axes.transData)
+
+    fill = kwargs.pop("fill", False)
+    pp = BboxPatch(rect, fill=fill, **kwargs)
+    parent_axes.add_patch(pp)
+
+    p1 = BboxConnector(inset_axes.bbox, rect, loc1=loc1, **kwargs)
+    inset_axes.add_patch(p1)
+    p1.set_clip_on(False)
+    p2 = BboxConnector(inset_axes.bbox, rect, loc1=loc2, **kwargs)
+    inset_axes.add_patch(p2)
+    p2.set_clip_on(False)
+
+    return pp, p1, p2
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/mpl_axes.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/mpl_axes.py
new file mode 100644
index 0000000000..aaff7b7692
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/mpl_axes.py
@@ -0,0 +1,154 @@
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import six
+
+import matplotlib.axes as maxes
+from matplotlib.artist import Artist
+from matplotlib.axis import XAxis, YAxis
+
+class SimpleChainedObjects(object):
+    def __init__(self, objects):
+        self._objects = objects
+
+    def __getattr__(self, k):
+        _a = SimpleChainedObjects([getattr(a, k) for a in self._objects])
+        return _a
+
+    def __call__(self, *kl, **kwargs):
+        for m in self._objects:
+            m(*kl, **kwargs)
+
+
+class Axes(maxes.Axes):
+
+    class AxisDict(dict):
+        def __init__(self, axes):
+            self.axes = axes
+            super(Axes.AxisDict, self).__init__()
+
+        def __getitem__(self, k):
+            if isinstance(k, tuple):
+                r = SimpleChainedObjects(
+                    [super(Axes.AxisDict, self).__getitem__(k1) for k1 in k])
+                return r
+            elif isinstance(k, slice):
+                if k.start is None and k.stop is None and k.step is None:
+                    r = SimpleChainedObjects(list(six.itervalues(self)))
+                    return r
+                else:
+                    raise ValueError("Unsupported slice")
+            else:
+                return dict.__getitem__(self, k)
+
+        def __call__(self, *v, **kwargs):
+            return maxes.Axes.axis(self.axes, *v, **kwargs)
+
+    def __init__(self, *kl, **kw):
+        super(Axes, self).__init__(*kl, **kw)
+
+    def _init_axis_artists(self, axes=None):
+        if axes is None:
+            axes = self
+
+        self._axislines = self.AxisDict(self)
+
+        self._axislines["bottom"] = SimpleAxisArtist(self.xaxis, 1, self.spines["bottom"])
+        self._axislines["top"] = SimpleAxisArtist(self.xaxis, 2, self.spines["top"])
+        self._axislines["left"] = SimpleAxisArtist(self.yaxis, 1, self.spines["left"])
+        self._axislines["right"] = SimpleAxisArtist(self.yaxis, 2, self.spines["right"])
+
+
+    def _get_axislines(self):
+        return self._axislines
+
+    axis = property(_get_axislines)
+
+    def cla(self):
+
+        super(Axes, self).cla()
+        self._init_axis_artists()
+
+
+class SimpleAxisArtist(Artist):
+    def __init__(self, axis, axisnum, spine):
+        self._axis = axis
+        self._axisnum = axisnum
+        self.line = spine
+
+        if isinstance(axis, XAxis):
+            self._axis_direction = ["bottom", "top"][axisnum-1]
+        elif isinstance(axis, YAxis):
+            self._axis_direction = ["left", "right"][axisnum-1]
+        else:
+            raise ValueError("axis must be instance of XAxis or YAxis : %s is provided" % (axis,))
+        Artist.__init__(self)
+
+
+    def _get_major_ticks(self):
+        tickline = "tick%dline" % self._axisnum
+        return SimpleChainedObjects([getattr(tick, tickline)
+                                     for tick in self._axis.get_major_ticks()])
+
+    def _get_major_ticklabels(self):
+        label = "label%d" % self._axisnum
+        return SimpleChainedObjects([getattr(tick, label)
+                                     for tick in self._axis.get_major_ticks()])
+
+    def _get_label(self):
+        return self._axis.label
+
+    major_ticks = property(_get_major_ticks)
+    major_ticklabels = property(_get_major_ticklabels)
+    label = property(_get_label)
+
+    def set_visible(self, b):
+        self.toggle(all=b)
+        self.line.set_visible(b)
+        self._axis.set_visible(True)
+        Artist.set_visible(self, b)
+
+    def set_label(self, txt):
+        self._axis.set_label_text(txt)
+
+    def toggle(self, all=None, ticks=None, ticklabels=None, label=None):
+
+        if all:
+            _ticks, _ticklabels, _label = True, True, True
+        elif all is not None:
+            _ticks, _ticklabels, _label = False, False, False
+        else:
+            _ticks, _ticklabels, _label = None, None, None
+
+        if ticks is not None:
+            _ticks = ticks
+        if ticklabels is not None:
+            _ticklabels = ticklabels
+        if label is not None:
+            _label = label
+
+        tickOn = "tick%dOn" % self._axisnum
+        labelOn = "label%dOn" % self._axisnum
+
+        if _ticks is not None:
+            tickparam = {tickOn: _ticks}
+            self._axis.set_tick_params(**tickparam)
+        if _ticklabels is not None:
+            tickparam = {labelOn: _ticklabels}
+            self._axis.set_tick_params(**tickparam)
+
+        if _label is not None:
+            pos = self._axis.get_label_position()
+            if (pos == self._axis_direction) and not _label:
+                self._axis.label.set_visible(False)
+            elif _label:
+                self._axis.label.set_visible(True)
+                self._axis.set_label_position(self._axis_direction)
+
+
+if __name__ == '__main__':
+    import matplotlib.pyplot as plt
+    fig = plt.figure()
+    ax = Axes(fig, [0.1, 0.1, 0.8, 0.8])
+    fig.add_axes(ax)
+    ax.cla()
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/parasite_axes.py b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/parasite_axes.py
new file mode 100644
index 0000000000..16a67b4d1f
--- /dev/null
+++ b/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/parasite_axes.py
@@ -0,0 +1,486 @@
+from __future__ import (absolute_import, division, print_function,
+                        unicode_literals)
+
+import six
+
+from matplotlib import (
+    artist as martist, collections as mcoll, transforms as mtransforms,
+    rcParams)
+from matplotlib.axes import subplot_class_factory
+from matplotlib.transforms import Bbox
+from .mpl_axes import Axes
+
+import numpy as np
+
+
+class ParasiteAxesBase(object):
+
+    def get_images_artists(self):
+        artists = {a for a in self.get_children() if a.get_visible()}
+        images = {a for a in self.images if a.get_visible()}
+
+        return list(images), list(artists - images)
+
+    def __init__(self, parent_axes, **kargs):
+
+        self._parent_axes = parent_axes
+        kargs.update(dict(frameon=False))
+        self._get_base_axes_attr("__init__")(self, parent_axes.figure,
+                                        parent_axes._position, **kargs)
+
+    def cla(self):
+        self._get_base_axes_attr("cla")(self)
+
+        martist.setp(self.get_children(), visible=False)
+        self._get_lines = self._parent_axes._get_lines
+
+        # In mpl's Axes, zorders of x- and y-axis are originally set
+        # within Axes.draw().
+        if self._axisbelow:
+            self.xaxis.set_zorder(0.5)
+            self.yaxis.set_zorder(0.5)
+        else:
+            self.xaxis.set_zorder(2.5)
+            self.yaxis.set_zorder(2.5)
+
+
+_parasite_axes_classes = {}
+def parasite_axes_class_factory(axes_class=None):
+    if axes_class is None:
+        axes_class = Axes
+
+    new_class = _parasite_axes_classes.get(axes_class)
+    if new_class is None:
+        def _get_base_axes_attr(self, attrname):
+            return getattr(axes_class, attrname)
+
+        new_class = type(str("%sParasite" % (axes_class.__name__)),
+                         (ParasiteAxesBase, axes_class),
+                         {'_get_base_axes_attr': _get_base_axes_attr})
+        _parasite_axes_classes[axes_class] = new_class
+
+    return new_class
+
+ParasiteAxes = parasite_axes_class_factory()
+
+# #class ParasiteAxes(ParasiteAxesBase, Axes):
+
+#     @classmethod
+#     def _get_base_axes_attr(cls, attrname):
+#         return getattr(Axes, attrname)
+
+
+
+class ParasiteAxesAuxTransBase(object):
+    def __init__(self, parent_axes, aux_transform, viewlim_mode=None,
+                 **kwargs):
+
+        self.transAux = aux_transform
+        self.set_viewlim_mode(viewlim_mode)
+
+        self._parasite_axes_class.__init__(self, parent_axes, **kwargs)
+
+    def _set_lim_and_transforms(self):
+
+        self.transAxes = self._parent_axes.transAxes
+
+        self.transData = \
+            self.transAux + \
+            self._parent_axes.transData
+
+        self._xaxis_transform = mtransforms.blended_transform_factory(
+                self.transData, self.transAxes)
+        self._yaxis_transform = mtransforms.blended_transform_factory(
+                self.transAxes, self.transData)
+
+    def set_viewlim_mode(self, mode):
+        if mode not in [None, "equal", "transform"]:
+            raise ValueError("Unknown mode : %s" % (mode,))
+        else:
+            self._viewlim_mode = mode
+
+    def get_viewlim_mode(self):
+        return self._viewlim_mode
+
+
+    def update_viewlim(self):
+        viewlim = self._parent_axes.viewLim.frozen()
+        mode = self.get_viewlim_mode()
+        if mode is None:
+            pass
+        elif mode == "equal":
+            self.axes.viewLim.set(viewlim)
+        elif mode == "transform":
+            self.axes.viewLim.set(viewlim.transformed(self.transAux.inverted()))
+        else:
+            raise ValueError("Unknown mode : %s" % (self._viewlim_mode,))
+
+
+    def _pcolor(self, method_name, *XYC, **kwargs):
+        if len(XYC) == 1:
+            C = XYC[0]
+            ny, nx = C.shape
+
+            gx = np.arange(-0.5, nx, 1.)
+            gy = np.arange(-0.5, ny, 1.)
+
+            X, Y = np.meshgrid(gx, gy)
+        else:
+            X, Y, C = XYC
+
+        pcolor_routine = self._get_base_axes_attr(method_name)
+
+        if "transform" in kwargs:
+            mesh = pcolor_routine(self, X, Y, C, **kwargs)
+        else:
+            orig_shape = X.shape
+            xy = np.vstack([X.flat, Y.flat])
+            xyt=xy.transpose()
+            wxy = self.transAux.transform(xyt)
+            gx, gy = wxy[:,0].reshape(orig_shape), wxy[:,1].reshape(orig_shape)
+            mesh = pcolor_routine(self, gx, gy, C, **kwargs)
+            mesh.set_transform(self._parent_axes.transData)
+
+        return mesh
+
+    def pcolormesh(self, *XYC, **kwargs):
+        return self._pcolor("pcolormesh", *XYC, **kwargs)
+
+    def pcolor(self, *XYC, **kwargs):
+        return self._pcolor("pcolor", *XYC, **kwargs)
+
+
+    def _contour(self, method_name, *XYCL, **kwargs):
+
+        if len(XYCL) <= 2:
+            C = XYCL[0]
+            ny, nx = C.shape
+
+            gx = np.arange(0., nx, 1.)
+            gy = np.arange(0., ny, 1.)
+
+            X,Y = np.meshgrid(gx, gy)
+            CL = XYCL
+        else:
+            X, Y = XYCL[:2]
+            CL = XYCL[2:]
+
+        contour_routine = self._get_base_axes_attr(method_name)
+
+        if "transform" in kwargs:
+            cont = contour_routine(self, X, Y, *CL, **kwargs)
+        else:
+            orig_shape = X.shape
+            xy = np.vstack([X.flat, Y.flat])
+            xyt=xy.transpose()
+            wxy = self.transAux.transform(xyt)
+            gx, gy = wxy[:,0].reshape(orig_shape), wxy[:,1].reshape(orig_shape)
+            cont = contour_routine(self, gx, gy, *CL, **kwargs)
+            for c in cont.collections:
+                c.set_transform(self._parent_axes.transData)
+
+        return cont
+
+    def contour(self, *XYCL, **kwargs):
+        return self._contour("contour", *XYCL, **kwargs)
+
+    def contourf(self, *XYCL, **kwargs):
+        return self._contour("contourf", *XYCL, **kwargs)
+
+    def apply_aspect(self, position=None):
+        self.update_viewlim()
+        self._get_base_axes_attr("apply_aspect")(self)
+        #ParasiteAxes.apply_aspect()
+
+
+
+_parasite_axes_auxtrans_classes = {}
+def parasite_axes_auxtrans_class_factory(axes_class=None):
+    if axes_class is None:
+        parasite_axes_class = ParasiteAxes
+    elif not issubclass(axes_class, ParasiteAxesBase):
+        parasite_axes_class = parasite_axes_class_factory(axes_class)
+    else:
+        parasite_axes_class = axes_class
+
+    new_class = _parasite_axes_auxtrans_classes.get(parasite_axes_class)
+    if new_class is None:
+        new_class = type(str("%sParasiteAuxTrans" % (parasite_axes_class.__name__)),
+                         (ParasiteAxesAuxTransBase, parasite_axes_class),
+                         {'_parasite_axes_class': parasite_axes_class,
+                         'name': 'parasite_axes'})
+        _parasite_axes_auxtrans_classes[parasite_axes_class] = new_class
+
+    return new_class
+
+
+ParasiteAxesAuxTrans = parasite_axes_auxtrans_class_factory(axes_class=ParasiteAxes)
+
+
+
+
+def _get_handles(ax):
+    handles = ax.lines[:]
+    handles.extend(ax.patches)
+    handles.extend([c for c in ax.collections
+                    if isinstance(c, mcoll.LineCollection)])
+    handles.extend([c for c in ax.collections
+                    if isinstance(c, mcoll.RegularPolyCollection)])
+    handles.extend([c for c in ax.collections
+                    if isinstance(c, mcoll.CircleCollection)])
+
+    return handles
+
+
+class HostAxesBase(object):
+    def __init__(self, *args, **kwargs):
+
+        self.parasites = []
+        self._get_base_axes_attr("__init__")(self, *args, **kwargs)
+
+
+    def get_aux_axes(self, tr, viewlim_mode="equal", axes_class=None):
+        parasite_axes_class = parasite_axes_auxtrans_class_factory(axes_class)
+        ax2 = parasite_axes_class(self, tr, viewlim_mode)
+        # note that ax2.transData == tr + ax1.transData
+        # Anthing you draw in ax2 will match the ticks and grids of ax1.
+        self.parasites.append(ax2)
+        ax2._remove_method = lambda h: self.parasites.remove(h)
+        return ax2
+
+    def _get_legend_handles(self, legend_handler_map=None):
+        # don't use this!
+        Axes_get_legend_handles = self._get_base_axes_attr("_get_legend_handles")
+        all_handles = list(Axes_get_legend_handles(self, legend_handler_map))
+
+        for ax in self.parasites:
+            all_handles.extend(ax._get_legend_handles(legend_handler_map))
+
+        return all_handles
+
+
+    def draw(self, renderer):
+
+        orig_artists = list(self.artists)
+        orig_images = list(self.images)
+
+        if hasattr(self, "get_axes_locator"):
+            locator = self.get_axes_locator()
+            if locator:
+                pos = locator(self, renderer)
+                self.set_position(pos, which="active")
+                self.apply_aspect(pos)
+            else:
+                self.apply_aspect()
+        else:
+            self.apply_aspect()
+
+        rect = self.get_position()
+
+        for ax in self.parasites:
+            ax.apply_aspect(rect)
+            images, artists = ax.get_images_artists()
+            self.images.extend(images)
+            self.artists.extend(artists)
+
+        self._get_base_axes_attr("draw")(self, renderer)
+        self.artists = orig_artists
+        self.images = orig_images
+
+
+    def cla(self):
+
+        for ax in self.parasites:
+            ax.cla()
+
+        self._get_base_axes_attr("cla")(self)
+        #super(HostAxes, self).cla()
+
+
+    def twinx(self, axes_class=None):
+        """
+        create a twin of Axes for generating a plot with a sharex
+        x-axis but independent y axis.  The y-axis of self will have
+        ticks on left and the returned axes will have ticks on the
+        right
+        """
+
+        if axes_class is None:
+            axes_class = self._get_base_axes()
+
+        parasite_axes_class = parasite_axes_class_factory(axes_class)
+
+        ax2 = parasite_axes_class(self, sharex=self, frameon=False)
+        self.parasites.append(ax2)
+
+        self.axis["right"].set_visible(False)
+
+        ax2.axis["right"].set_visible(True)
+        ax2.axis["left", "top", "bottom"].set_visible(False)
+
+        def _remove_method(h):
+            self.parasites.remove(h)
+            self.axis["right"].set_visible(True)
+            self.axis["right"].toggle(ticklabels=False, label=False)
+        ax2._remove_method = _remove_method
+
+        return ax2
+
+    def twiny(self, axes_class=None):
+        """
+        create a twin of Axes for generating a plot with a shared
+        y-axis but independent x axis.  The x-axis of self will have
+        ticks on bottom and the returned axes will have ticks on the
+        top
+        """
+
+        if axes_class is None:
+            axes_class = self._get_base_axes()
+
+        parasite_axes_class = parasite_axes_class_factory(axes_class)
+
+        ax2 = parasite_axes_class(self, sharey=self, frameon=False)
+        self.parasites.append(ax2)
+
+        self.axis["top"].set_visible(False)
+
+        ax2.axis["top"].set_visible(True)
+        ax2.axis["left", "right", "bottom"].set_visible(False)
+
+        def _remove_method(h):
+            self.parasites.remove(h)
+            self.axis["top"].set_visible(True)
+            self.axis["top"].toggle(ticklabels=False, label=False)
+        ax2._remove_method = _remove_method
+
+        return ax2
+
+
+    def twin(self, aux_trans=None, axes_class=None):
+        """
+        create a twin of Axes for generating a plot with a sharex
+        x-axis but independent y axis.  The y-axis of self will have
+        ticks on left and the returned axes will have ticks on the
+        right
+        """
+
+        if axes_class is None:
+            axes_class = self._get_base_axes()
+
+        parasite_axes_auxtrans_class = parasite_axes_auxtrans_class_factory(axes_class)
+
+        if aux_trans is None:
+            ax2 = parasite_axes_auxtrans_class(self, mtransforms.IdentityTransform(),
+                                               viewlim_mode="equal",
+                                               )
+        else:
+            ax2 = parasite_axes_auxtrans_class(self, aux_trans,
+                                               viewlim_mode="transform",
+                                               )
+        self.parasites.append(ax2)
+        ax2._remove_method = lambda h: self.parasites.remove(h)
+
+        self.axis["top", "right"].set_visible(False)
+
+        ax2.axis["top", "right"].set_visible(True)
+        ax2.axis["left", "bottom"].set_visible(False)
+
+        def _remove_method(h):
+            self.parasites.remove(h)
+            self.axis["top", "right"].set_visible(True)
+            self.axis["top", "right"].toggle(ticklabels=False, label=False)
+        ax2._remove_method = _remove_method
+
+        return ax2
+
+    def get_tightbbox(self, renderer, call_axes_locator=True):
+
+        bbs = [ax.get_tightbbox(renderer, call_axes_locator)
+               for ax in self.parasites]
+        get_tightbbox = self._get_base_axes_attr("get_tightbbox")
+        bbs.append(get_tightbbox(self, renderer, call_axes_locator))
+
+        _bbox = Bbox.union([b for b in bbs if b.width!=0 or b.height!=0])
+
+        return _bbox
+
+
+
+_host_axes_classes = {}
+def host_axes_class_factory(axes_class=None):
+    if axes_class is None:
+        axes_class = Axes
+
+    new_class = _host_axes_classes.get(axes_class)
+    if new_class is None:
+        def _get_base_axes(self):
+            return axes_class
+
+        def _get_base_axes_attr(self, attrname):
+            return getattr(axes_class, attrname)
+
+        new_class = type(str("%sHostAxes" % (axes_class.__name__)),
+                         (HostAxesBase, axes_class),
+                         {'_get_base_axes_attr': _get_base_axes_attr,
+                          '_get_base_axes': _get_base_axes})
+
+        _host_axes_classes[axes_class] = new_class
+
+    return new_class
+
+def host_subplot_class_factory(axes_class):
+    host_axes_class = host_axes_class_factory(axes_class=axes_class)
+    subplot_host_class = subplot_class_factory(host_axes_class)
+    return subplot_host_class
+
+HostAxes = host_axes_class_factory(axes_class=Axes)
+SubplotHost = subplot_class_factory(HostAxes)
+
+
+def host_axes(*args, **kwargs):
+    """
+    Create axes that can act as a hosts to parasitic axes.
+
+    Parameters
+    ----------
+    figure : `matplotlib.figure.Figure`
+        Figure to which the axes will be added. Defaults to the current figure
+        `pyplot.gcf()`.
+
+    *args, **kwargs :
+        Will be passed on to the underlying ``Axes`` object creation.
+    """
+    import matplotlib.pyplot as plt
+    axes_class = kwargs.pop("axes_class", None)
+    host_axes_class = host_axes_class_factory(axes_class)
+    fig = kwargs.get("figure", None)
+    if fig is None:
+        fig = plt.gcf()
+    ax = host_axes_class(fig, *args, **kwargs)
+    fig.add_axes(ax)
+    plt.draw_if_interactive()
+    return ax
+
+def host_subplot(*args, **kwargs):
+    """
+    Create a subplot that can act as a host to parasitic axes.
+
+    Parameters
+    ----------
+    figure : `matplotlib.figure.Figure`
+        Figure to which the subplot will be added. Defaults to the current
+        figure `pyplot.gcf()`.
+
+    *args, **kwargs :
+        Will be passed on to the underlying ``Axes`` object creation.
+    """
+    import matplotlib.pyplot as plt
+    axes_class = kwargs.pop("axes_class", None)
+    host_subplot_class = host_subplot_class_factory(axes_class)
+    fig = kwargs.get("figure", None)
+    if fig is None:
+        fig = plt.gcf()
+    ax = host_subplot_class(fig, *args, **kwargs)
+    fig.add_subplot(ax)
+    plt.draw_if_interactive()
+    return ax
-- 
cgit v1.2.3