diff options
author | shumkovnd <shumkovnd@yandex-team.com> | 2023-11-10 14:39:34 +0300 |
---|---|---|
committer | shumkovnd <shumkovnd@yandex-team.com> | 2023-11-10 16:42:24 +0300 |
commit | 77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch) | |
tree | c51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/matplotlib/py2/mpl_toolkits/axisartist/floating_axes.py | |
parent | dd6d20cadb65582270ac23f4b3b14ae189704b9d (diff) | |
download | ydb-77eb2d3fdcec5c978c64e025ced2764c57c00285.tar.gz |
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/matplotlib/py2/mpl_toolkits/axisartist/floating_axes.py')
-rw-r--r-- | contrib/python/matplotlib/py2/mpl_toolkits/axisartist/floating_axes.py | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/contrib/python/matplotlib/py2/mpl_toolkits/axisartist/floating_axes.py b/contrib/python/matplotlib/py2/mpl_toolkits/axisartist/floating_axes.py new file mode 100644 index 0000000000..468413dbac --- /dev/null +++ b/contrib/python/matplotlib/py2/mpl_toolkits/axisartist/floating_axes.py @@ -0,0 +1,544 @@ +""" +An experimental support for curvilinear grid. +""" +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import six +from six.moves import zip + +# TODO : +# see if tick_iterator method can be simplified by reusing the parent method. + +import numpy as np + +from matplotlib.transforms import Affine2D, IdentityTransform +from . import grid_helper_curvelinear +from .axislines import AxisArtistHelper, GridHelperBase +from .axis_artist import AxisArtist +from .grid_finder import GridFinder + + +class FloatingAxisArtistHelper(grid_helper_curvelinear.FloatingAxisArtistHelper): + pass + + +class FixedAxisArtistHelper(grid_helper_curvelinear.FloatingAxisArtistHelper): + + def __init__(self, grid_helper, side, nth_coord_ticks=None): + """ + nth_coord = along which coordinate value varies. + nth_coord = 0 -> x axis, nth_coord = 1 -> y axis + """ + + value, nth_coord = grid_helper.get_data_boundary(side) # return v= 0 , nth=1, extremes of the other coordinate. + super(FixedAxisArtistHelper, self).__init__(grid_helper, + nth_coord, + value, + axis_direction=side, + ) + #self.grid_helper = grid_helper + if nth_coord_ticks is None: + nth_coord_ticks = nth_coord + self.nth_coord_ticks = nth_coord_ticks + + self.value = value + self.grid_helper = grid_helper + self._side = side + + + def update_lim(self, axes): + self.grid_helper.update_lim(axes) + + self.grid_info = self.grid_helper.grid_info + + + + def get_axislabel_pos_angle(self, axes): + + extremes = self.grid_info["extremes"] + + if self.nth_coord == 0: + xx0 = self.value + yy0 = (extremes[2]+extremes[3])/2. + dxx, dyy = 0., abs(extremes[2]-extremes[3])/1000. + elif self.nth_coord == 1: + xx0 = (extremes[0]+extremes[1])/2. + yy0 = self.value + dxx, dyy = abs(extremes[0]-extremes[1])/1000., 0. + + grid_finder = self.grid_helper.grid_finder + xx1, yy1 = grid_finder.transform_xy([xx0], [yy0]) + + trans_passingthrough_point = axes.transData + axes.transAxes.inverted() + p = trans_passingthrough_point.transform_point([xx1[0], yy1[0]]) + + + if (0. <= p[0] <= 1.) and (0. <= p[1] <= 1.): + xx1c, yy1c = axes.transData.transform_point([xx1[0], yy1[0]]) + xx2, yy2 = grid_finder.transform_xy([xx0+dxx], [yy0+dyy]) + xx2c, yy2c = axes.transData.transform_point([xx2[0], yy2[0]]) + + return (xx1c, yy1c), np.arctan2(yy2c-yy1c, xx2c-xx1c)/np.pi*180. + else: + return None, None + + + + def get_tick_transform(self, axes): + return IdentityTransform() #axes.transData + + def get_tick_iterators(self, axes): + """tick_loc, tick_angle, tick_label, (optionally) tick_label""" + + + grid_finder = self.grid_helper.grid_finder + + lat_levs, lat_n, lat_factor = self.grid_info["lat_info"] + lon_levs, lon_n, lon_factor = self.grid_info["lon_info"] + + lon_levs, lat_levs = np.asarray(lon_levs), np.asarray(lat_levs) + if lat_factor is not None: + yy0 = lat_levs / lat_factor + dy = 0.001 / lat_factor + else: + yy0 = lat_levs + dy = 0.001 + + if lon_factor is not None: + xx0 = lon_levs / lon_factor + dx = 0.001 / lon_factor + else: + xx0 = lon_levs + dx = 0.001 + + _extremes = self.grid_helper._extremes + xmin, xmax = sorted(_extremes[:2]) + ymin, ymax = sorted(_extremes[2:]) + if self.nth_coord == 0: + mask = (ymin <= yy0) & (yy0 <= ymax) + yy0 = yy0[mask] + elif self.nth_coord == 1: + mask = (xmin <= xx0) & (xx0 <= xmax) + xx0 = xx0[mask] + + def transform_xy(x, y): + x1, y1 = grid_finder.transform_xy(x, y) + x2y2 = axes.transData.transform(np.array([x1, y1]).transpose()) + x2, y2 = x2y2.transpose() + return x2, y2 + + # find angles + if self.nth_coord == 0: + xx0 = np.empty_like(yy0) + xx0.fill(self.value) + + #yy0_ = yy0.copy() + + xx1, yy1 = transform_xy(xx0, yy0) + + xx00 = xx0.astype(float, copy=True) + xx00[xx0+dx>xmax] -= dx + xx1a, yy1a = transform_xy(xx00, yy0) + xx1b, yy1b = transform_xy(xx00+dx, yy0) + + yy00 = yy0.astype(float, copy=True) + yy00[yy0+dy>ymax] -= dy + xx2a, yy2a = transform_xy(xx0, yy00) + xx2b, yy2b = transform_xy(xx0, yy00+dy) + + labels = self.grid_info["lat_labels"] + labels = [l for l, m in zip(labels, mask) if m] + + elif self.nth_coord == 1: + yy0 = np.empty_like(xx0) + yy0.fill(self.value) + + #xx0_ = xx0.copy() + xx1, yy1 = transform_xy(xx0, yy0) + + + yy00 = yy0.astype(float, copy=True) + yy00[yy0+dy>ymax] -= dy + xx1a, yy1a = transform_xy(xx0, yy00) + xx1b, yy1b = transform_xy(xx0, yy00+dy) + + xx00 = xx0.astype(float, copy=True) + xx00[xx0+dx>xmax] -= dx + xx2a, yy2a = transform_xy(xx00, yy0) + xx2b, yy2b = transform_xy(xx00+dx, yy0) + + labels = self.grid_info["lon_labels"] + labels = [l for l, m in zip(labels, mask) if m] + + + def f1(): + dd = np.arctan2(yy1b-yy1a, xx1b-xx1a) # angle normal + dd2 = np.arctan2(yy2b-yy2a, xx2b-xx2a) # angle tangent + mm = ((yy1b-yy1a)==0.) & ((xx1b-xx1a)==0.) # mask where dd1 is not defined + dd[mm] = dd2[mm] + np.pi / 2 + + #dd += np.pi + #dd = np.arctan2(xx2-xx1, angle_tangent-yy1) + trans_tick = self.get_tick_transform(axes) + tr2ax = trans_tick + axes.transAxes.inverted() + for x, y, d, d2, lab in zip(xx1, yy1, dd, dd2, labels): + c2 = tr2ax.transform_point((x, y)) + delta=0.00001 + if (0. -delta<= c2[0] <= 1.+delta) and \ + (0. -delta<= c2[1] <= 1.+delta): + d1 = d/3.14159*180. + d2 = d2/3.14159*180. + #_mod = (d2-d1+180)%360 + #if _mod < 180: + # d1 += 180 + ##_div, _mod = divmod(d2-d1, 360) + yield [x, y], d1, d2, lab + #, d2/3.14159*180.+da) + + return f1(), iter([]) + + def get_line_transform(self, axes): + return axes.transData + + def get_line(self, axes): + + self.update_lim(axes) + from matplotlib.path import Path + k, v = dict(left=("lon_lines0", 0), + right=("lon_lines0", 1), + bottom=("lat_lines0", 0), + top=("lat_lines0", 1))[self._side] + + xx, yy = self.grid_info[k][v] + return Path(np.column_stack([xx, yy])) + + + +from .grid_finder import ExtremeFinderSimple + +class ExtremeFinderFixed(ExtremeFinderSimple): + def __init__(self, extremes): + self._extremes = extremes + + def __call__(self, transform_xy, x1, y1, x2, y2): + """ + get extreme values. + + x1, y1, x2, y2 in image coordinates (0-based) + nx, ny : number of division in each axis + """ + #lon_min, lon_max, lat_min, lat_max = self._extremes + return self._extremes + + + +class GridHelperCurveLinear(grid_helper_curvelinear.GridHelperCurveLinear): + + def __init__(self, aux_trans, extremes, + grid_locator1=None, + grid_locator2=None, + tick_formatter1=None, + tick_formatter2=None): + """ + aux_trans : a transform from the source (curved) coordinate to + target (rectilinear) coordinate. An instance of MPL's Transform + (inverse transform should be defined) or a tuple of two callable + objects which defines the transform and its inverse. The callables + need take two arguments of array of source coordinates and + should return two target coordinates: + e.g., *x2, y2 = trans(x1, y1)* + """ + + self._old_values = None + + self._extremes = extremes + extreme_finder = ExtremeFinderFixed(extremes) + + super(GridHelperCurveLinear, self).__init__(aux_trans, + extreme_finder, + grid_locator1=grid_locator1, + grid_locator2=grid_locator2, + tick_formatter1=tick_formatter1, + tick_formatter2=tick_formatter2) + + + # def update_grid_finder(self, aux_trans=None, **kw): + + # if aux_trans is not None: + # self.grid_finder.update_transform(aux_trans) + + # self.grid_finder.update(**kw) + # self.invalidate() + + + # def _update(self, x1, x2, y1, y2): + # "bbox in 0-based image coordinates" + # # update wcsgrid + + # if self.valid() and self._old_values == (x1, x2, y1, y2): + # return + + # self._update_grid(x1, y1, x2, y2) + + # self._old_values = (x1, x2, y1, y2) + + # self._force_update = False + + + def get_data_boundary(self, side): + """ + return v= 0 , nth=1 + """ + lon1, lon2, lat1, lat2 = self._extremes + return dict(left=(lon1, 0), + right=(lon2, 0), + bottom=(lat1, 1), + top=(lat2, 1))[side] + + + def new_fixed_axis(self, loc, + nth_coord=None, + axis_direction=None, + offset=None, + axes=None): + + if axes is None: + axes = self.axes + + if axis_direction is None: + axis_direction = loc + + _helper = FixedAxisArtistHelper(self, loc, + nth_coord_ticks=nth_coord) + + + axisline = AxisArtist(axes, _helper, axis_direction=axis_direction) + axisline.line.set_clip_on(True) + axisline.line.set_clip_box(axisline.axes.bbox) + + + return axisline + + + # new_floating_axis will inherit the grid_helper's extremes. + + # def new_floating_axis(self, nth_coord, + # value, + # axes=None, + # axis_direction="bottom" + # ): + + # axis = super(GridHelperCurveLinear, + # self).new_floating_axis(nth_coord, + # value, axes=axes, + # axis_direction=axis_direction) + + # # set extreme values of the axis helper + # if nth_coord == 1: + # axis.get_helper().set_extremes(*self._extremes[:2]) + # elif nth_coord == 0: + # axis.get_helper().set_extremes(*self._extremes[2:]) + + # return axis + + + def _update_grid(self, x1, y1, x2, y2): + + #self.grid_info = self.grid_finder.get_grid_info(x1, y1, x2, y2) + + if self.grid_info is None: + self.grid_info = dict() + + grid_info = self.grid_info + + grid_finder = self.grid_finder + extremes = grid_finder.extreme_finder(grid_finder.inv_transform_xy, + x1, y1, x2, y2) + + lon_min, lon_max = sorted(extremes[:2]) + lat_min, lat_max = sorted(extremes[2:]) + lon_levs, lon_n, lon_factor = \ + grid_finder.grid_locator1(lon_min, lon_max) + lat_levs, lat_n, lat_factor = \ + grid_finder.grid_locator2(lat_min, lat_max) + grid_info["extremes"] = lon_min, lon_max, lat_min, lat_max #extremes + + grid_info["lon_info"] = lon_levs, lon_n, lon_factor + grid_info["lat_info"] = lat_levs, lat_n, lat_factor + + grid_info["lon_labels"] = grid_finder.tick_formatter1("bottom", + lon_factor, + lon_levs) + + grid_info["lat_labels"] = grid_finder.tick_formatter2("bottom", + lat_factor, + lat_levs) + + if lon_factor is None: + lon_values = np.asarray(lon_levs[:lon_n]) + else: + lon_values = np.asarray(lon_levs[:lon_n]/lon_factor) + if lat_factor is None: + lat_values = np.asarray(lat_levs[:lat_n]) + else: + lat_values = np.asarray(lat_levs[:lat_n]/lat_factor) + + lon_values0 = lon_values[(lon_min<lon_values) & (lon_values<lon_max)] + lat_values0 = lat_values[(lat_min<lat_values) & (lat_values<lat_max)] + lon_lines, lat_lines = grid_finder._get_raw_grid_lines(lon_values0, + lat_values0, + lon_min, lon_max, + lat_min, lat_max) + + + grid_info["lon_lines"] = lon_lines + grid_info["lat_lines"] = lat_lines + + + lon_lines, lat_lines = grid_finder._get_raw_grid_lines(extremes[:2], + extremes[2:], + *extremes) + #lon_min, lon_max, + # lat_min, lat_max) + + + grid_info["lon_lines0"] = lon_lines + grid_info["lat_lines0"] = lat_lines + + + + def get_gridlines(self, which="major", axis="both"): + grid_lines = [] + if axis in ["both", "x"]: + for gl in self.grid_info["lon_lines"]: + grid_lines.extend([gl]) + if axis in ["both", "y"]: + for gl in self.grid_info["lat_lines"]: + grid_lines.extend([gl]) + + return grid_lines + + + def get_boundary(self): + """ + return Nx2 array of x,y coordinate of the boundary + """ + x0, x1, y0, y1 = self._extremes + tr = self._aux_trans + xx = np.linspace(x0, x1, 100) + yy0, yy1 = np.empty_like(xx), np.empty_like(xx) + yy0.fill(y0) + yy1.fill(y1) + + yy = np.linspace(y0, y1, 100) + xx0, xx1 = np.empty_like(yy), np.empty_like(yy) + xx0.fill(x0) + xx1.fill(x1) + + xxx = np.concatenate([xx[:-1], xx1[:-1], xx[-1:0:-1], xx0]) + yyy = np.concatenate([yy0[:-1], yy[:-1], yy1[:-1], yy[::-1]]) + t = tr.transform(np.array([xxx, yyy]).transpose()) + + return t + + + + + + + + + + + + +class FloatingAxesBase(object): + + + def __init__(self, *kl, **kwargs): + grid_helper = kwargs.get("grid_helper", None) + if grid_helper is None: + raise ValueError("FloatingAxes requires grid_helper argument") + if not hasattr(grid_helper, "get_boundary"): + raise ValueError("grid_helper must implement get_boundary method") + + self._axes_class_floating.__init__(self, *kl, **kwargs) + + self.set_aspect(1.) + self.adjust_axes_lim() + + + def _gen_axes_patch(self): + """ + Returns the patch used to draw the background of the axes. It + is also used as the clipping path for any data elements on the + axes. + + In the standard axes, this is a rectangle, but in other + projections it may not be. + + .. note:: + Intended to be overridden by new projection types. + """ + import matplotlib.patches as mpatches + grid_helper = self.get_grid_helper() + t = grid_helper.get_boundary() + return mpatches.Polygon(t) + + def cla(self): + self._axes_class_floating.cla(self) + #HostAxes.cla(self) + self.patch.set_transform(self.transData) + + + patch = self._axes_class_floating._gen_axes_patch(self) + patch.set_figure(self.figure) + patch.set_visible(False) + patch.set_transform(self.transAxes) + + self.patch.set_clip_path(patch) + self.gridlines.set_clip_path(patch) + + self._original_patch = patch + + + def adjust_axes_lim(self): + + #t = self.get_boundary() + grid_helper = self.get_grid_helper() + t = grid_helper.get_boundary() + x, y = t[:,0], t[:,1] + + xmin, xmax = min(x), max(x) + ymin, ymax = min(y), max(y) + + dx = (xmax-xmin)/100. + dy = (ymax-ymin)/100. + + self.set_xlim(xmin-dx, xmax+dx) + self.set_ylim(ymin-dy, ymax+dy) + + + +_floatingaxes_classes = {} + +def floatingaxes_class_factory(axes_class): + + new_class = _floatingaxes_classes.get(axes_class) + if new_class is None: + new_class = type(str("Floating %s" % (axes_class.__name__)), + (FloatingAxesBase, axes_class), + {'_axes_class_floating': axes_class}) + _floatingaxes_classes[axes_class] = new_class + + return new_class + +from .axislines import Axes +from mpl_toolkits.axes_grid1.parasite_axes import host_axes_class_factory + +FloatingAxes = floatingaxes_class_factory(host_axes_class_factory(Axes)) + + +import matplotlib.axes as maxes +FloatingSubplot = maxes.subplot_class_factory(FloatingAxes) |