aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1
diff options
context:
space:
mode:
authorshumkovnd <shumkovnd@yandex-team.com>2023-11-10 14:39:34 +0300
committershumkovnd <shumkovnd@yandex-team.com>2023-11-10 16:42:24 +0300
commit77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch)
treec51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1
parentdd6d20cadb65582270ac23f4b3b14ae189704b9d (diff)
downloadydb-77eb2d3fdcec5c978c64e025ced2764c57c00285.tar.gz
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1')
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/__init__.py12
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/anchored_artists.py376
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_divider.py975
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_grid.py771
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_rgb.py228
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/axes_size.py323
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/colorbar.py836
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/inset_locator.py659
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/mpl_axes.py154
-rw-r--r--contrib/python/matplotlib/py2/mpl_toolkits/axes_grid1/parasite_axes.py486
10 files changed, 4820 insertions, 0 deletions
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