Ejemplo n.º 1
0
def getTransform(parentMap, node): # test, speed up by caching transform for node
   n = node
   transform = Affine2D()
   while True:
      tString = n.get("transform")
      if tString is not None:
         for m in reversed(list(finditer(r"(?P<method>\w+)\(\s*(?P<args>[^)]*)\)", tString))):
            args = m.group('args').replace(",", " ").split()
            method = m.group('method')
            if method == "matrix":
               transform = composite_transform_factory(transform, Affine2D.from_values(*args))
            elif method == "translate":
               transform.translate(args[0], 0 if len(args)<2 else args[1])
            elif method == "scale":
               transform.scale(*args)
            elif method == "rotate":
               if len(args) == 1:
                  transform.rotate_deg(args[0])
               else:
                  transform.rotate_deg_around(*args)
            elif method == "skewX":
               transform.skew_deg(args[0], 0)
            elif method == "skewY":
               transform.skew_deg(0, args[0])
      if n in parentMap:
         n = parentMap[n]
      else: break
   return transform
Ejemplo n.º 2
0
def fit_affine_clip(xy1f, xy2f):
    sol = fit_affine(xy1f, xy2f)

    affine_tr = Affine2D.from_values(*sol)

    xy1f_tr = affine_tr.transform(xy1f) #[:,0], xy1f[:,1])

    # mask and refit
    dx_ = xy1f_tr[:,0] - xy2f[:,0]
    mystd = dx_.std()
    mm = [np.abs(dx_) < 3. * mystd]

    sol = fit_affine(xy1f[mm], xy2f[mm])

    affine_tr = Affine2D.from_values(*sol)

    return affine_tr, mm
Ejemplo n.º 3
0
def triangular_axes(fig):
	# ternary projection
	tr = Affine2D.from_values(1., 0, 0.5, np.sqrt(3)/2., 0, 0)
	# negative ternary projection for dependent axis
	neg_tr = Affine2D.from_values(1., 0, -0.5, np.sqrt(3)/2., 0, 0)
	# identity transform
	identity_tr = Affine2D.from_values(1, 0, 0, 1, 0, 0)

	grid_helper = GridHelperTriangular(tr, 
				    extremes=(0,1,0,1), 
				    grid_type="independent")
	# use null_locator to kill extra horizontal gridlines from dependent axis
	null_locator = grid_finder.MaxNLocator(1)
	dep_grid_helper = GridHelperTriangular(neg_tr, 
					extremes=(0,1,0,1), 
					grid_type="dependent", 
					grid_locator2=null_locator)

	# Add independent axes with gridlines
	ax1 = floating_axes.FloatingSubplot(fig, 111, grid_helper=grid_helper)
	
	fig.add_subplot(ax1)

	ax1.axis[:].set_visible(False)
	ax1.axis["bottom"].set_visible(True)
	ax1.axis["left"].set_visible(True)

	# Add dependent axis with gridlines
	ax2 = ParasiteAxesAuxTrans(ax1, 
			    identity_tr, 
			    "equal", 
			    grid_helper=dep_grid_helper)
	ax2.axis["right"] = ax2.get_grid_helper().new_floating_axis(0,
							     1,
							     axes=ax1)
	ax2.axis["right"].toggle(ticklabels=False)
	ax1.parasites.append(ax2)
	ax1.grid(True)
	ax2.grid(True)
	ax1.plot([])
	ax1.set_aspect(1.)
	return ax1
Ejemplo n.º 4
0
def test_Affine2D_from_values():
    points = [ [0,0],
               [10,20],
               [-1,0],
               ]

    t = Affine2D.from_values(1,0,0,0,0,0)
    actual = t.transform(points)
    expected = np.array( [[0,0],[10,0],[-1,0]] )
    assert_almost_equal(actual,expected)

    t = Affine2D.from_values(0,2,0,0,0,0)
    actual = t.transform(points)
    expected = np.array( [[0,0],[0,20],[0,-2]] )
    assert_almost_equal(actual,expected)

    t = Affine2D.from_values(0,0,3,0,0,0)
    actual = t.transform(points)
    expected = np.array( [[0,0],[60,0],[0,0]] )
    assert_almost_equal(actual,expected)

    t = Affine2D.from_values(0,0,0,4,0,0)
    actual = t.transform(points)
    expected = np.array( [[0,0],[0,80],[0,0]] )
    assert_almost_equal(actual,expected)

    t = Affine2D.from_values(0,0,0,0,5,0)
    actual = t.transform(points)
    expected = np.array( [[5,0],[5,0],[5,0]] )
    assert_almost_equal(actual,expected)

    t = Affine2D.from_values(0,0,0,0,0,6)
    actual = t.transform(points)
    expected = np.array( [[0,6],[0,6],[0,6]] )
    assert_almost_equal(actual,expected)
Ejemplo n.º 5
0
 def device_fill_points(self, points, mode):
     if mode in (FILL, FILL_STROKE, EOF_FILL_STROKE, EOF_FILL):
         fill = tuple(self.state.fill_color)
     else:
         fill = None
     if mode in (STROKE, FILL_STROKE, EOF_FILL_STROKE):
         color = tuple(self.state.line_color)
     else:
         color = tuple(self.state.fill_color)
     path = Path(points)
     gc = self._backend.new_gc()
     gc.set_linewidth(self.state.line_width)
     if not (self.state.line_dash[1] == 0).all():
         gc.set_dashes(self.state.line_dash[0], list(self.state.line_dash[1]))
     if self.state.clipping_path:
         gc.set_clip_path(self._get_transformed_clip_path())
     gc.set_joinstyle(line_join_map[self.state.line_join])
     gc.set_capstyle(line_cap_map[self.state.line_cap])
     gc.set_foreground(color, isRGB=True)
     gc.set_alpha(self.state.alpha)
     transform = Affine2D.from_values(*affine.affine_params(self.get_ctm()))
     self._backend.draw_path(gc, path, transform, fill)
     gc.restore()
Ejemplo n.º 6
0
    def device_draw_image(self, img, rect):
        """
        draw_image(img_gc, rect=(x,y,w,h))

        Draws another gc into this one.  If 'rect' is not provided, then
        the image gc is drawn into this one, rooted at (0,0) and at full
        pixel size.  If 'rect' is provided, then the image is resized
        into the (w,h) given and drawn into this GC at point (x,y).

        img_gc is either a Numeric array (WxHx3 or WxHx4) or a GC from Kiva's
        Agg backend (kiva.agg.GraphicsContextArray).

        Requires the Python Imaging Library (PIL).
        """
        from PIL import Image as PilImage
        from matplotlib import _image

        # We turn img into a PIL object, since that is what ReportLab
        # requires.  To do this, we first determine if the input image
        # GC needs to be converted to RGBA/RGB.  If so, we see if we can
        # do it nicely (using convert_pixel_format), and if not, we do
        # it brute-force using Agg.
        if type(img) == type(array([])):
            # Numeric array
            converted_img = agg.GraphicsContextArray(img, pix_format='rgba32')
            format = 'RGBA'
        elif isinstance(img, agg.GraphicsContextArray):
            if img.format().startswith('RGBA'):
                format = 'RGBA'
            elif img.format().startswith('RGB'):
                format = 'RGB'
            else:
                converted_img = img.convert_pixel_format('rgba32', inplace=0)
                format = 'RGBA'
            # Should probably take this into account
            # interp = img.get_image_interpolation()
        else:
            warnings.warn("Cannot render image of type %r into SVG context."
                          % type(img))
            return

        if rect == None:
            rect = (0, 0, img.width(), img.height())

        width, height = img.width(), img.height()

        # converted_img now holds an Agg graphics context with the image
        pil_img = PilImage.fromstring(format,
                                      (converted_img.width(),
                                       converted_img.height()),
                                      converted_img.bmp_array.tostring())

        left, top, width, height = rect
        if width != img.width() or height != img.height():
            # This is not strictly required.
            pil_img = pil_img.resize((int(width), int(height)), PilImage.NEAREST)
        pil_img = pil_img.transpose(PilImage.FLIP_TOP_BOTTOM)
        # Fix for the SVG backend, which seems to flip x when a transform is provided.
        if self._backend.flipy():
            pil_img = pil_img.transpose(PilImage.FLIP_LEFT_RIGHT)

        mpl_img = _image.frombuffer(pil_img.tostring(), width, height, True)
        mpl_img.is_grayscale = False

        gc = self._backend.new_gc()
        if self.state.clipping_path:
            gc.set_clip_path(self._get_transformed_clip_path())
        transform = Affine2D.from_values(*affine.affine_params(self.get_ctm()))
        self._backend.draw_image(gc, left, top, mpl_img,
                                 dx=width, dy=height, transform=transform)
        gc.restore()
Ejemplo n.º 7
0
 def _make_flip_transform(self, transform):
     return (transform +
             Affine2D().scale(1.0, -1.0).translate(0.0, self.height))
Ejemplo n.º 8
0
    def _set_lim_and_transforms(self):
        """
        This is called once when the plot is created to set up all the
        transforms for the data, text and grids.
        """
        # There are three important coordinate spaces going on here:
        #
        #    1. Data space: The space of the data itself
        #
        #    2. Axes space: The unit rectangle (0, 0) to (1, 1)
        #       covering the entire plot area.
        #
        #    3. Display space: The coordinates of the resulting image,
        #       often in pixels or dpi/inch.

        # This function makes heavy use of the Transform classes in
        # ``lib/matplotlib/transforms.py.`` For more information, see
        # the inline documentation there.

        # The goal of the first two transformations is to get from the
        # data space (in this case longitude and latitude) to axes
        # space.  It is separated into a non-affine and affine part so
        # that the non-affine part does not have to be recomputed when
        # a simple affine change to the figure has been made (such as
        # resizing the window or changing the dpi).

        # 1) The core transformation from data space into
        # rectilinear space defined in the HammerTransform class.
        self.transProjection = IdentityTransform()
        # 2) The above has an output range that is not in the unit
        # rectangle, so scale and translate it so it fits correctly
        # within the axes.  The peculiar calculations of xscale and
        # yscale are specific to a Aitoff-Hammer projection, so don't
        # worry about them too much.
        self.transAffine = Affine2D.from_values(
                1., 0, 0.5, np.sqrt(3)/2., 0, 0)
        self.transAffinedep = Affine2D.from_values(
                1., 0, -0.5, np.sqrt(3)/2., 0, 0)
        #self.transAffine = IdentityTransform()
        
        # 3) This is the transformation from axes space to display
        # space.
        self.transAxes = BboxTransformTo(self.bbox)

        # Now put these 3 transforms together -- from data all the way
        # to display coordinates.  Using the '+' operator, these
        # transforms will be applied "in order".  The transforms are
        # automatically simplified, if possible, by the underlying
        # transformation framework.
        self.transData = \
            self.transProjection + \
            self.transAffine + \
            self.transAxes

        # The main data transformation is set up.  Now deal with
        # gridlines and tick labels.

        # Longitude gridlines and ticklabels.  The input to these
        # transforms are in display space in x and axes space in y.
        # Therefore, the input values will be in range (-xmin, 0),
        # (xmax, 1).  The goal of these transforms is to go from that
        # space to display space.  The tick labels will be offset 4
        # pixels from the equator.

        self._xaxis_pretransform = IdentityTransform()
        self._xaxis_transform = \
            self._xaxis_pretransform + \
            self.transData
        self._xaxis_text1_transform = \
            Affine2D().scale(1.0, 0.0) + \
            self.transData + \
            Affine2D().translate(0.0, -20.0)
        self._xaxis_text2_transform = \
            Affine2D().scale(1.0, 0.0) + \
            self.transData + \
            Affine2D().translate(0.0, -4.0)

        # Now set up the transforms for the latitude ticks.  The input to
        # these transforms are in axes space in x and display space in
        # y.  Therefore, the input values will be in range (0, -ymin),
        # (1, ymax).  The goal of these transforms is to go from that
        # space to display space.  The tick labels will be offset 4
        # pixels from the edge of the axes ellipse.

        self._yaxis_transform = self.transData
        yaxis_text_base = \
            self.transProjection + \
            (self.transAffine + \
             self.transAxes)
        self._yaxis_text1_transform = \
            yaxis_text_base + \
            Affine2D().translate(-8.0, 0.0)
        self._yaxis_text2_transform = \
            yaxis_text_base + \
            Affine2D().translate(8.0, 0.0)
Ejemplo n.º 9
0
def volume_overlay3(ax, quotes,
                    colorup='k', colordown='r',
                    width=4, alpha=1.0):
    """Add a volume overlay to the current axes.  quotes is a list of (d,
    open, high, low, close, volume) and close-open is used to
    determine the color of the bar
    Parameters
    ----------
    ax : `Axes`
        an Axes instance to plot to
    quotes : sequence of (time, open, high, low, close, ...) sequences
        data to plot.  time must be in float date format - see date2num
    width : int
        the bar width in points
    colorup : color
        the color of the lines where close1 >= close0
    colordown : color
        the color of the lines where close1 <  close0
    alpha : float
         bar transparency
    Returns
    -------
    ret : `barCollection`
        The `barrCollection` added to the axes
    """

    colorup = mcolors.to_rgba(colorup, alpha)
    colordown = mcolors.to_rgba(colordown, alpha)
    colord = {True: colorup, False: colordown}

    dates, opens, highs, lows, closes, volumes = list(zip(*quotes))
    colors = [colord[close1 >= close0]
              for close0, close1 in zip(closes[:-1], closes[1:])
              if close0 != -1 and close1 != -1]
    colors.insert(0, colord[closes[0] >= opens[0]])

    right = width / 2.0
    left = -width / 2.0

    bars = [((left, 0), (left, volume), (right, volume), (right, 0))
            for d, open, high, low, close, volume in quotes]

    sx = ax.figure.dpi * (1.0 / 72.0)  # scale for points
    sy = ax.bbox.height / ax.viewLim.height

    barTransform = Affine2D().scale(sx, sy)

    dates = [d for d, open, high, low, close, volume in quotes]
    offsetsBars = [(d, 0) for d in dates]

    useAA = 0,  # use tuple here
    lw = 0.5,   # and here
    barCollection = PolyCollection(bars,
                                   facecolors=colors,
                                   edgecolors=((0, 0, 0, 1),),
                                   antialiaseds=useAA,
                                   linewidths=lw,
                                   offsets=offsetsBars,
                                   transOffset=ax.transData,
                                   )
    barCollection.set_transform(barTransform)

    minpy, maxx = (min(dates), max(dates))
    miny = 0
    maxy = max([volume for d, open, high, low, close, volume in quotes])
    corners = (minpy, miny), (maxx, maxy)
    ax.update_datalim(corners)
    # print 'datalim', ax.dataLim.bounds
    # print 'viewlim', ax.viewLim.bounds

    ax.add_collection(barCollection)
    ax.autoscale_view()

    return barCollection
Ejemplo n.º 10
0
def ros_map(ax: plt.Axes,
            yaml_path: str,
            plot_mode: PlotMode,
            cmap: str = "Greys_r",
            mask_unknown_value: int = SETTINGS.ros_map_unknown_cell_value,
            alpha: float = SETTINGS.ros_map_alpha_value) -> None:
    """
    Inserts an image of an 2D ROS map into the plot axis.
    See: http://wiki.ros.org/map_server#Map_format
    :param ax: 2D matplotlib axes
    :param plot_mode: a 2D PlotMode
    :param yaml_path: yaml file that contains the metadata of the map image
    :param cmap: color map used to map scalar data to colors
    :param mask_unknown_value: uint8 value that represents unknown cells.
                               If specified, these cells will be masked out.
                               If set to None or False, nothing will be masked.
    """
    import yaml

    if isinstance(ax, Axes3D):
        raise PlotException("ros_map can't be drawn into a 3D axis")
    if plot_mode in {PlotMode.xz, PlotMode.yz, PlotMode.zx, PlotMode.zy}:
        # Image lies in xy / yx plane, nothing to see here.
        return
    x_idx, y_idx, _ = plot_mode_to_idx(plot_mode)

    with open(yaml_path) as f:
        metadata = yaml.safe_load(f)

    # Load map image, mask unknown cells if desired.
    image_path = metadata["image"]
    if not os.path.isabs(image_path):
        image_path = os.path.join(os.path.dirname(yaml_path), image_path)
    image = plt.imread(image_path)
    if mask_unknown_value:
        mask_unknown_value = np.uint8(mask_unknown_value)
        image = np.ma.masked_where(image == mask_unknown_value, image)

    # Squeeze extent to reflect metric coordinates.
    resolution = metadata["resolution"]
    n_rows, n_cols = image.shape[x_idx], image.shape[y_idx]
    extent = [0, n_cols * resolution, 0, n_rows * resolution]
    if plot_mode == PlotMode.yx:
        image = np.rot90(image)
        image = np.fliplr(image)
    ax_image = ax.imshow(image,
                         origin="upper",
                         cmap=cmap,
                         extent=extent,
                         zorder=1,
                         alpha=alpha)

    # Transform map frame to plot axis origin.
    map_to_pixel_origin = Affine2D()
    map_to_pixel_origin.translate(metadata["origin"][x_idx],
                                  metadata["origin"][y_idx])
    angle = metadata["origin"][2]
    if plot_mode == PlotMode.yx:
        # Rotation axis (z) points downwards.
        angle *= -1
    map_to_pixel_origin.rotate(angle)
    ax_image.set_transform(map_to_pixel_origin + ax.transData)

    # Initially flipped axes are lost for mysterious reasons...
    if SETTINGS.plot_invert_xaxis:
        ax.invert_xaxis()
    if SETTINGS.plot_invert_yaxis:
        ax.invert_yaxis()
Ejemplo n.º 11
0
import numpy as np

%matplotlib widget

px = np.random.rand(10)
py = np.random.rand(10)

offsets = np.ma.column_stack([px, py])

#%% 1
fig, ax = plt.subplots()

reduction = 50
c = Path.unit_circle()
c = c.transformed(Affine2D().scale(0.5 * reduction))

collection = PathCollection(
    (c,), offsets=offsets, 
    transOffset = ax.transData,
    edgecolor='black', 
    facecolor=(0, 0, 0, .0125),
    linewidth=1
)

collection.set_transform(IdentityTransform())
ax.add_collection( collection )

#%% 2

collection.set_path_effects([withStroke(linewidth=5, foreground='r')])
Ejemplo n.º 12
0
def create_line_switch_collection(net, size=1, distance_to_bus=3, use_line_geodata=False, **kwargs):
    """
    Creates a matplotlib patch collection of pandapower switches.

    INPUT:
        **net** (pandapowerNet) - The pandapower network

    OPTIONAL:

        **size** (float, 1) - Size of the switch patches

        **distance_to_bus** (float, 3) - Distance of the switch patch from the bus patch

        **use_line_geodata** (bool, False) - If True, line coordinates are used to identify the
                                             switch position

        **kwargs - Key word arguments are passed to the patch function

    """
    lbs_switches = net.switch.index[net.switch.et == "l"]

    color = kwargs.pop("color", "k")

    switch_patches = []
    for switch in lbs_switches:
        sb = net.switch.bus.loc[switch]
        line = net.line.loc[net.switch.element.loc[switch]]
        fb = line.from_bus
        tb = line.to_bus

        line_buses = set([fb, tb])
        target_bus = list(line_buses - set([sb]))[0]

        if sb not in net.bus_geodata.index or target_bus not in net.bus_geodata.index:
            logger.warning("Bus coordinates for switch %s not found, skipped switch!" % switch)
            continue

        # switch bus and target coordinates
        pos_sb = net.bus_geodata.loc[sb, ["x", "y"]].values
        pos_ta = np.zeros(2)

        use_bus_geodata = False

        if use_line_geodata:
            if line.name in net.line_geodata.index:
                line_coords = net.line_geodata.coords.loc[line.name]
                # check, which end of the line is nearer to the switch bus
                if len(line_coords) > 2:
                    if abs(line_coords[0][0] - pos_sb[0]) < 0.01 and \
                            abs(line_coords[0][1] - pos_sb[1]) < 0.01:
                        pos_ta = np.array([line_coords[1][0], line_coords[1][1]])
                    else:
                        pos_ta = np.array([line_coords[-2][0], line_coords[-2][1]])
                else:
                    use_bus_geodata = True
            else:
                use_bus_geodata = True

        if not use_line_geodata or use_bus_geodata:
            pos_ta = net.bus_geodata.loc[target_bus, ["x", "y"]]

        # position of switch symbol
        vec = pos_ta - pos_sb
        mag = np.linalg.norm(vec)
        pos_sw = pos_sb + vec / mag * distance_to_bus

        # rotation of switch symbol
        angle = np.arctan2(vec[1], vec[0])
        rotation = Affine2D().rotate_around(pos_sw[0], pos_sw[1], angle)

        # color switch by state
        col = color if net.switch.closed.loc[switch] else "white"

        # create switch patch (switch size is respected to center the switch on the line)
        patch = Rectangle((pos_sw[0] - size/2, pos_sw[1] - size/2), size, size, facecolor=col,
                          edgecolor=color)
        # apply rotation
        patch.set_transform(rotation)

        switch_patches.append(patch)

    switches = PatchCollection(switch_patches, match_original=True, **kwargs)
    return switches
Ejemplo n.º 13
0
def create_cg(st, fig=None, subplot=111):
    """ Helper function to create curvelinear grid

    The function makes use of the Matplotlib AXISARTIST namespace
    `mpl_toolkits.axisartist \
    <https://matplotlib.org/mpl_toolkits/axes_grid/users/axisartist.html>`_.

    Here are some limitations to normal Matplotlib Axes. While using the
    Matplotlib `AxesGrid Toolkit \
    <https://matplotlib.org/mpl_toolkits/axes_grid/index.html>`_
    most of the limitations can be overcome.
    See `Matplotlib AxesGrid Toolkit User’s Guide \
    <https://matplotlib.org/mpl_toolkits/axes_grid/users/index.html>`_.

    Parameters
    ----------
    st : string
        scan type, 'PPI' or 'RHI'
    fig : matplotlib Figure object
        If given, the PPI will be plotted into this figure object. Axes are
        created as needed. If None a new figure object will be created or
        current figure will be used, depending on "subplot".
    subplot : :class:`matplotlib:matplotlib.gridspec.GridSpec`, \
        matplotlib grid definition
        nrows/ncols/plotnumber, see examples section
        defaults to '111', only one subplot

    Returns
    -------
    cgax : matplotlib toolkit axisartist Axes object
        curvelinear Axes (r-theta-grid)
    caax : matplotlib Axes object (twin to cgax)
        Cartesian Axes (x-y-grid) for plotting cartesian data
    paax : matplotlib Axes object (parasite to cgax)
        The parasite axes object for plotting polar data
    """

    if st == 'RHI':
        # create transformation
        tr = Affine2D().scale(np.pi / 180, 1) + PolarAxes.PolarTransform()

        # build up curvelinear grid
        extreme_finder = ah.ExtremeFinderCycle(20, 20,
                                               lon_cycle=100,
                                               lat_cycle=None,
                                               lon_minmax=(0, np.inf),
                                               lat_minmax=(0, np.inf),
                                               )

        # locator and formatter for angular annotation
        grid_locator1 = ah.LocatorDMS(10.)
        tick_formatter1 = ah.FormatterDMS()

        # grid_helper for curvelinear grid
        grid_helper = GridHelperCurveLinear(tr,
                                            extreme_finder=extreme_finder,
                                            grid_locator1=grid_locator1,
                                            grid_locator2=None,
                                            tick_formatter1=tick_formatter1,
                                            tick_formatter2=None,
                                            )

        # try to set nice locations for range gridlines
        grid_helper.grid_finder.grid_locator2._nbins = 30.0
        grid_helper.grid_finder.grid_locator2._steps = [0, 1, 1.5,
                                                        2, 2.5, 5, 10]

    if st == 'PPI':
        # Set theta start to north
        tr_rotate = Affine2D().translate(-90, 0)
        # set theta running clockwise
        tr_scale = Affine2D().scale(-np.pi / 180, 1)
        # create transformation
        tr = tr_rotate + tr_scale + PolarAxes.PolarTransform()

        # build up curvelinear grid
        extreme_finder = ah.ExtremeFinderCycle(20, 20,
                                               lon_cycle=360,
                                               lat_cycle=None,
                                               lon_minmax=(360, 0),
                                               lat_minmax=(0, np.inf),
                                               )

        # locator and formatter for angle annotation
        locs = [i for i in np.arange(0., 359., 10.)]
        grid_locator1 = FixedLocator(locs)
        tick_formatter1 = DictFormatter(dict([(i, r"${0:.0f}^\circ$".format(i))
                                              for i in locs]))

        # grid_helper for curvelinear grid
        grid_helper = GridHelperCurveLinear(tr,
                                            extreme_finder=extreme_finder,
                                            grid_locator1=grid_locator1,
                                            grid_locator2=None,
                                            tick_formatter1=tick_formatter1,
                                            tick_formatter2=None,
                                            )
        # try to set nice locations for range gridlines
        grid_helper.grid_finder.grid_locator2._nbins = 15.0
        grid_helper.grid_finder.grid_locator2._steps = [0, 1, 1.5, 2,
                                                        2.5,
                                                        5,
                                                        10]

    # if there is no figure object given
    if fig is None:
        # create new figure if there is only one subplot
        if subplot is 111:
            fig = pl.figure()
        # otherwise get current figure or create new figure
        else:
            fig = pl.gcf()

    # generate Axis
    cgax = SubplotHost(fig, subplot, grid_helper=grid_helper)

    fig.add_axes(cgax)

    # PPIs always plottetd with equal aspect
    if st == 'PPI':
        cgax.set_aspect('equal', adjustable='box')

    # get twin axis for cartesian grid
    caax = cgax.twin()
    # move axis annotation from right to left and top to bottom for
    # cartesian axis
    caax.toggle_axisline()

    # make right and top axis visible and show ticklabels (curvelinear axis)
    cgax.axis["top", "right"].set_visible(True)
    cgax.axis["top", "right"].major_ticklabels.set_visible(True)

    # make ticklabels of left and bottom axis invisible (curvelinear axis)
    cgax.axis["left", "bottom"].major_ticklabels.set_visible(False)

    # and also set tickmarklength to zero for better presentation
    # (curvelinear axis)
    cgax.axis["top", "right", "left", "bottom"].major_ticks.set_ticksize(0)

    # show theta (angles) on top and right axis
    cgax.axis["top"].get_helper().nth_coord_ticks = 0
    cgax.axis["right"].get_helper().nth_coord_ticks = 0

    # generate and add parasite axes with given transform
    paax = ParasiteAxesAuxTrans(cgax, tr, "equal")
    # note that paax.transData == tr + cgax.transData
    # Anything you draw in paax will match the ticks and grids of cgax.
    cgax.parasites.append(paax)

    return cgax, caax, paax
Ejemplo n.º 14
0
def curvelinear_test3(fig):
    """
    polar projection, but in a rectangular box.
    """
    global ax1, axis
    import numpy as np
    from . import angle_helper
    from matplotlib.projections import PolarAxes
    from matplotlib.transforms import Affine2D

    from mpl_toolkits.axes_grid.parasite_axes import SubplotHost

    # PolarAxes.PolarTransform takes radian. However, we want our coordinate
    # system in degree
    tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform()

    # polar projection, which involves cycle, and also has limits in
    # its coordinates, needs a special method to find the extremes
    # (min, max of the coordinate within the view).

    # 20, 20 : number of sampling points along x, y direction
    extreme_finder = angle_helper.ExtremeFinderCycle(
        20,
        20,
        lon_cycle=360,
        lat_cycle=None,
        lon_minmax=None,
        lat_minmax=(0, np.inf),
    )

    grid_locator1 = angle_helper.LocatorDMS(12)
    # Find a grid values appropriate for the coordinate (degree,
    # minute, second).

    tick_formatter1 = angle_helper.FormatterDMS()
    # And also uses an appropriate formatter.  Note that,the
    # acceptable Locator and Formatter class is a bit different than
    # that of mpl's, and you cannot directly use mpl's Locator and
    # Formatter here (but may be possible in the future).

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1)

    ax1 = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)

    for axis in list(six.itervalues(ax1.axis)):
        axis.set_visible(False)

    fig.add_subplot(ax1)

    grid_helper = ax1.get_grid_helper()
    ax1.axis["lat1"] = axis = grid_helper.new_floating_axis(
        0, 130, axes=ax1, axis_direction="left")
    axis.label.set_text("Test")
    axis.label.set_visible(True)
    axis.get_helper()._extremes = 0.001, 10

    grid_helper = ax1.get_grid_helper()
    ax1.axis["lat2"] = axis = grid_helper.new_floating_axis(
        0, 50, axes=ax1, axis_direction="right")
    axis.label.set_text("Test")
    axis.label.set_visible(True)
    axis.get_helper()._extremes = 0.001, 10

    ax1.axis["lon"] = axis = grid_helper.new_floating_axis(
        1, 10, axes=ax1, axis_direction="bottom")
    axis.label.set_text("Test 2")
    axis.get_helper()._extremes = 50, 130
    axis.major_ticklabels.set_axis_direction("top")
    axis.label.set_axis_direction("top")

    grid_helper.grid_finder.grid_locator1.den = 5
    grid_helper.grid_finder.grid_locator2._nbins = 5

    #     # A parasite axes with given transform
    #     ax2 = ParasiteAxesAuxTrans(ax1, tr, "equal")
    #     # note that ax2.transData == tr + ax1.transData
    #     # Anthing you draw in ax2 will match the ticks and grids of ax1.
    #     ax1.parasites.append(ax2)
    #     intp = cbook.simple_linear_interpolation
    #     ax2.plot(intp(np.array([0, 30]), 50),
    #              intp(np.array([10., 10.]), 50))

    ax1.set_aspect(1.)
    ax1.set_xlim(-5, 12)
    ax1.set_ylim(-5, 10)

    ax1.grid(True)
Ejemplo n.º 15
0
def plot_day_summary2(
    ax,
    opens,
    closes,
    highs,
    lows,
    ticksize=4,
    colorup='k',
    colordown='r',
):
    """

    Represent the time, open, close, high, low as a vertical line
    ranging from low to high.  The left tick is the open and the right
    tick is the close.

    ax          : an Axes instance to plot to
    ticksize    : size of open and close ticks in points
    colorup     : the color of the lines where close >= open
    colordown   : the color of the lines where close <  open

    return value is a list of lines added
    """

    # note this code assumes if any value open, close, low, high is
    # missing they all are missing

    rangeSegments = [((i, low), (i, high))
                     for i, low, high in zip(xrange(len(lows)), lows, highs)
                     if low != -1]

    # the ticks will be from ticksize to 0 in points at the origin and
    # we'll translate these to the i, close location
    openSegments = [((-ticksize, 0), (0, 0))]

    # the ticks will be from 0 to ticksize in points at the origin and
    # we'll translate these to the i, close location
    closeSegments = [((0, 0), (ticksize, 0))]

    offsetsOpen = [(i, open) for i, open in zip(xrange(len(opens)), opens)
                   if open != -1]

    offsetsClose = [(i, close) for i, close in zip(xrange(len(closes)), closes)
                    if close != -1]

    scale = ax.figure.dpi * (1.0 / 72.0)

    tickTransform = Affine2D().scale(scale, 0.0)

    r, g, b = colorConverter.to_rgb(colorup)
    colorup = r, g, b, 1
    r, g, b = colorConverter.to_rgb(colordown)
    colordown = r, g, b, 1
    colord = {
        True: colorup,
        False: colordown,
    }
    colors = [
        colord[open < close] for open, close in zip(opens, closes)
        if open != -1 and close != -1
    ]

    assert (len(rangeSegments) == len(offsetsOpen))
    assert (len(offsetsOpen) == len(offsetsClose))
    assert (len(offsetsClose) == len(colors))

    useAA = 0,  # use tuple here
    lw = 1,  # and here
    rangeCollection = LineCollection(
        rangeSegments,
        colors=colors,
        linewidths=lw,
        antialiaseds=useAA,
    )

    openCollection = LineCollection(
        openSegments,
        colors=colors,
        antialiaseds=useAA,
        linewidths=lw,
        offsets=offsetsOpen,
        transOffset=ax.transData,
    )
    openCollection.set_transform(tickTransform)

    closeCollection = LineCollection(
        closeSegments,
        colors=colors,
        antialiaseds=useAA,
        linewidths=lw,
        offsets=offsetsClose,
        transOffset=ax.transData,
    )
    closeCollection.set_transform(tickTransform)

    minpy, maxx = (0, len(rangeSegments))
    miny = min([low for low in lows if low != -1])
    maxy = max([high for high in highs if high != -1])
    corners = (minpy, miny), (maxx, maxy)
    ax.update_datalim(corners)
    ax.autoscale_view()

    # add these last
    ax.add_collection(rangeCollection)
    ax.add_collection(openCollection)
    ax.add_collection(closeCollection)
    return rangeCollection, openCollection, closeCollection
Ejemplo n.º 16
0
def volume_overlay3(ax,
                    quotes,
                    colorup='k',
                    colordown='r',
                    width=4,
                    alpha=1.0):
    """
    Add a volume overlay to the current axes.  quotes is a list of (d,
    open, close, high, low, volume) and close-open is used to
    determine the color of the bar

    kwarg
    width       : the bar width in points
    colorup     : the color of the lines where close1 >= close0
    colordown   : the color of the lines where close1 <  close0
    alpha       : bar transparency


    """

    r, g, b = colorConverter.to_rgb(colorup)
    colorup = r, g, b, alpha
    r, g, b = colorConverter.to_rgb(colordown)
    colordown = r, g, b, alpha
    colord = {
        True: colorup,
        False: colordown,
    }

    dates, opens, closes, highs, lows, volumes = zip(*quotes)
    colors = [
        colord[close1 >= close0]
        for close0, close1 in zip(closes[:-1], closes[1:])
        if close0 != -1 and close1 != -1
    ]
    colors.insert(0, colord[closes[0] >= opens[0]])

    right = width / 2.0
    left = -width / 2.0

    bars = [((left, 0), (left, volume), (right, volume), (right, 0))
            for d, open, close, high, low, volume in quotes]

    sx = ax.figure.dpi * (1.0 / 72.0)  # scale for points
    sy = ax.bbox.height / ax.viewLim.height

    barTransform = Affine2D().scale(sx, sy)

    dates = [d for d, open, close, high, low, volume in quotes]
    offsetsBars = [(d, 0) for d in dates]

    useAA = 0,  # use tuple here
    lw = 0.5,  # and here
    barCollection = PolyCollection(
        bars,
        facecolors=colors,
        edgecolors=((0, 0, 0, 1), ),
        antialiaseds=useAA,
        linewidths=lw,
        offsets=offsetsBars,
        transOffset=ax.transData,
    )
    barCollection.set_transform(barTransform)

    minpy, maxx = (min(dates), max(dates))
    miny = 0
    maxy = max([volume for d, open, close, high, low, volume in quotes])
    corners = (minpy, miny), (maxx, maxy)
    ax.update_datalim(corners)
    #print 'datalim', ax.dataLim.bounds
    #print 'viewlim', ax.viewLim.bounds

    ax.add_collection(barCollection)
    ax.autoscale_view()

    return barCollection
Ejemplo n.º 17
0
    def _set_lim_and_transforms(self):
        # A (possibly non-linear) projection on the (already scaled) data

        # There are three important coordinate spaces going on here:
        #
        #    1. Data space: The space of the data itself
        #
        #    2. Axes space: The unit rectangle (0, 0) to (1, 1)
        #       covering the entire plot area.
        #
        #    3. Display space: The coordinates of the resulting image,
        #       often in pixels or dpi/inch.

        # This function makes heavy use of the Transform classes in
        # ``lib/matplotlib/transforms.py.`` For more information, see
        # the inline documentation there.

        # The goal of the first two transformations is to get from the
        # data space (in this case longitude and latitude) to axes
        # space.  It is separated into a non-affine and affine part so
        # that the non-affine part does not have to be recomputed when
        # a simple affine change to the figure has been made (such as
        # resizing the window or changing the dpi).

        # 1) The core transformation from data space into
        # rectilinear space defined in the HammerTransform class.
        self.transProjection = self._get_core_transform(self.RESOLUTION)

        # 2) The above has an output range that is not in the unit
        # rectangle, so scale and translate it so it fits correctly
        # within the axes.  The peculiar calculations of xscale and
        # yscale are specific to a Aitoff-Hammer projection, so don't
        # worry about them too much.
        self.transAffine = self._get_affine_transform()

        # 3) This is the transformation from axes space to display
        # space.
        self.transAxes = BboxTransformTo(self.bbox)

        # Now put these 3 transforms together -- from data all the way
        # to display coordinates.  Using the '+' operator, these
        # transforms will be applied "in order".  The transforms are
        # automatically simplified, if possible, by the underlying
        # transformation framework.
        self.transData = \
            self.transProjection + \
            self.transAffine + \
            self.transAxes

        # The main data transformation is set up.  Now deal with
        # gridlines and tick labels.

        # Longitude gridlines and ticklabels.  The input to these
        # transforms are in display space in x and axes space in y.
        # Therefore, the input values will be in range (-xmin, 0),
        # (xmax, 1).  The goal of these transforms is to go from that
        # space to display space.  The tick labels will be offset 4
        # pixels from the equator.
        self._xaxis_pretransform = \
            Affine2D() \
            .scale(1.0, self._longitude_cap * 2.0) \
            .translate(0.0, -self._longitude_cap)
        self._xaxis_transform = \
            self._xaxis_pretransform + \
            self.transData
        self._xaxis_text1_transform = \
            Affine2D().scale(1.0, 0.0) + \
            self.transData + \
            Affine2D().translate(0.0, 4.0)
        self._xaxis_text2_transform = \
            Affine2D().scale(1.0, 0.0) + \
            self.transData + \
            Affine2D().translate(0.0, -4.0)

        # Now set up the transforms for the latitude ticks.  The input to
        # these transforms are in axes space in x and display space in
        # y.  Therefore, the input values will be in range (0, -ymin),
        # (1, ymax).  The goal of these transforms is to go from that
        # space to display space.  The tick labels will be offset 4
        # pixels from the edge of the axes ellipse.
        yaxis_stretch = Affine2D().scale(np.pi * 2, 1).translate(-np.pi, 0)
        yaxis_space = Affine2D().scale(1.0, 1.1)
        self._yaxis_transform = \
            yaxis_stretch + \
            self.transData
        yaxis_text_base = \
            yaxis_stretch + \
            self.transProjection + \
            (yaxis_space +
             self.transAffine +
             self.transAxes)
        self._yaxis_text1_transform = \
            yaxis_text_base + \
            Affine2D().translate(-8.0, 0.0)
        self._yaxis_text2_transform = \
            yaxis_text_base + \
            Affine2D().translate(8.0, 0.0)
Ejemplo n.º 18
0
def curved_earth_axes(rect=111,
                      fig=None,
                      minground=0.,
                      maxground=2000,
                      minalt=0,
                      maxalt=500,
                      Re=6371.,
                      nyticks=5,
                      nxticks=4):
    """
    Create curved axes in ground-range and altitude
    Copied from DaViTPy: https://github.com/vtsuperdarn/davitpy/blob/1b578ea2491888e3d97d6e0a8bc6d8cc7c9211fb/davitpy/utils/plotUtils.py#L559
    """
    from matplotlib.transforms import Affine2D, Transform
    import mpl_toolkits.axisartist.floating_axes as floating_axes
    from matplotlib.projections import polar
    from mpl_toolkits.axisartist.grid_finder import FixedLocator, DictFormatter
    import numpy as np
    from pylab import gcf

    ang = maxground / Re
    minang = minground / Re
    angran = ang - minang
    angle_ticks = [(0, "{:.0f}".format(minground))]
    while angle_ticks[-1][0] < angran:
        tang = angle_ticks[-1][0] + 1. / nxticks * angran
        angle_ticks.append((tang, "{:.0f}".format((tang - minang) * Re)))
    grid_locator1 = FixedLocator([v for v, s in angle_ticks])
    tick_formatter1 = DictFormatter(dict(angle_ticks))

    altran = float(maxalt - minalt)
    alt_ticks = [(minalt + Re, "{:.0f}".format(minalt))]

    while alt_ticks[-1][0] < Re + maxalt:
        alt_ticks.append(
            (altran / float(nyticks) + alt_ticks[-1][0],
             "{:.0f}".format(altran / float(nyticks) + alt_ticks[-1][0] - Re)))
    _ = alt_ticks.pop()
    grid_locator2 = FixedLocator([v for v, s in alt_ticks])
    tick_formatter2 = DictFormatter(dict(alt_ticks))

    tr_rotate = Affine2D().rotate(np.pi / 2 - ang / 2)
    tr_shift = Affine2D().translate(0, Re)
    tr = polar.PolarTransform() + tr_rotate

    grid_helper = \
        floating_axes.GridHelperCurveLinear(tr, extremes=(0, angran, Re+minalt,
                                                          Re+maxalt),
                                            grid_locator1=grid_locator1,
                                            grid_locator2=grid_locator2,
                                            tick_formatter1=tick_formatter1,
                                            tick_formatter2=tick_formatter2,)

    if not fig:
        fig = gcf()
    ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)

    # adjust axis
    #     ax1.axis["left"].label.set_text(r"Alt. [km]")
    #     ax1.axis["bottom"].label.set_text(r"Ground range [km]")
    ax1.invert_xaxis()

    ax1.minground = minground
    ax1.maxground = maxground
    ax1.minalt = minalt
    ax1.maxalt = maxalt
    ax1.Re = Re

    fig.add_subplot(ax1, transform=tr)

    # create a parasite axes whose transData in RA, cz
    aux_ax = ax1.get_aux_axes(tr)

    # for aux_ax to have a clip path as in ax
    aux_ax.patch = ax1.patch

    # but this has a side effect that the patch is drawn twice, and possibly
    # over some other artists. So, we decrease the zorder a bit to prevent this.
    ax1.patch.zorder = 0.9

    return ax1, aux_ax
Ejemplo n.º 19
0
    def draw_image(self, gc, x, y, im, transform=None):
        # docstring inherited

        h, w = im.shape[:2]

        if w == 0 or h == 0:
            return

        attrib = {}
        clipid = self._get_clip(gc)
        if clipid is not None:
            # Can't apply clip-path directly to the image because the
            # image has a transformation, which would also be applied
            # to the clip-path
            self.writer.start('g', attrib={'clip-path': 'url(#%s)' % clipid})

        oid = gc.get_gid()
        url = gc.get_url()
        if url is not None:
            self.writer.start('a', attrib={'xlink:href': url})
        if rcParams['svg.image_inline']:
            bytesio = io.BytesIO()
            _png.write_png(im, bytesio)
            oid = oid or self._make_id('image', bytesio.getvalue())
            attrib['xlink:href'] = (
                "data:image/png;base64,\n" +
                base64.b64encode(bytesio.getvalue()).decode('ascii'))
        else:
            self._imaged[self.basename] = (self._imaged.get(self.basename, 0) +
                                           1)
            filename = '%s.image%d.png' % (self.basename,
                                           self._imaged[self.basename])
            _log.info('Writing image file for inclusion: %s', filename)
            _png.write_png(im, filename)
            oid = oid or 'Im_' + self._make_id('image', filename)
            attrib['xlink:href'] = filename

        attrib['id'] = oid

        if transform is None:
            w = 72.0 * w / self.image_dpi
            h = 72.0 * h / self.image_dpi

            self.writer.element('image',
                                transform=generate_transform([
                                    ('scale', (1, -1)), ('translate', (0, -h))
                                ]),
                                x=short_float_fmt(x),
                                y=short_float_fmt(-(self.height - y - h)),
                                width=short_float_fmt(w),
                                height=short_float_fmt(h),
                                attrib=attrib)
        else:
            alpha = gc.get_alpha()
            if alpha != 1.0:
                attrib['opacity'] = short_float_fmt(alpha)

            flipped = (Affine2D().scale(1.0 / w, 1.0 / h) + transform +
                       Affine2D().translate(x, y).scale(1.0, -1.0).translate(
                           0.0, self.height))

            attrib['transform'] = generate_transform([('matrix',
                                                       flipped.frozen())])
            self.writer.element('image',
                                width=short_float_fmt(w),
                                height=short_float_fmt(h),
                                attrib=attrib)

        if url is not None:
            self.writer.end('a')
        if clipid is not None:
            self.writer.end('g')
Ejemplo n.º 20
0
def get_slice_axes(fig,
                   num,
                   R,
                   Rb,
                   gs=None,
                   lat1=-90.0,
                   lat2=90.0,
                   shrink=1.0):

    tr_rotate = Affine2D().translate(0, 90)
    # set up polar axis
    tr = PolarAxes.PolarTransform()  #+ tr_rotate

    angle_ticks = [(np.radians(i), str(i)) for i in np.arange(80, -81, -10)]
    # angle_ticks = [(np.radians(80), r"$80$"),
    #                (np.radians(40), r"$\frac{3}{4}\pi$"),
    #                (1.*np.pi, r"$\pi$"),
    #                (1.25*np.pi, r"$\frac{5}{4}\pi$"),
    #                (1.5*np.pi, r"$\frac{3}{2}\pi$"),
    #                (1.75*np.pi, r"$\frac{7}{4}\pi$")]

    # set up ticks and spacing around the circle
    grid_locator1 = FixedLocator([v for v, s in angle_ticks])
    tick_formatter1 = DictFormatter(dict(angle_ticks))

    # set up grid spacing along the 'radius'
    radius_ticks = [
        (Rb, ''),
        # (1.5, '%i' % (MAX_R/2.)),
        (R, '')
    ]

    grid_locator2 = FixedLocator([v for v, s in radius_ticks])
    tick_formatter2 = DictFormatter(dict(radius_ticks))

    # define angle ticks around the circumference:
    # set up axis:
    # tr: the polar axis setup
    # extremes: theta max, theta min, r max, r min
    # the grid for the theta axis
    # the grid for the r axis
    # the tick formatting for the theta axis
    # the tick formatting for the r axis
    l1 = np.radians(lat1)
    l2 = np.radians(lat2)
    grid_helper = floating_axes.GridHelperCurveLinear(
        tr,
        extremes=(l1, l2, R, Rb),
        grid_locator1=grid_locator1,
        grid_locator2=grid_locator2,
        tick_formatter1=tick_formatter1,
        tick_formatter2=tick_formatter2)

    if type(num) is tuple:
        ax1 = floating_axes.FloatingSubplot(fig,
                                            num[0],
                                            num[1],
                                            num[2],
                                            grid_helper=grid_helper)
    else:
        if gs is not None:
            ax1 = floating_axes.FloatingSubplot(fig,
                                                gs,
                                                grid_helper=grid_helper)
        else:
            ax1 = floating_axes.FloatingSubplot(fig,
                                                num,
                                                grid_helper=grid_helper)

    fig.add_subplot(ax1)

    ax1.axis["left"].set_axis_direction("bottom")
    ax1.axis["right"].set_axis_direction("bottom")
    ax1.axis["left"].major_ticklabels.set_rotation(90)
    ax1.axis["bottom"].set_axis_direction("right")
    ax1.axis["bottom"].major_ticks.set_tick_out(True)
    #  ax1.axis["bottom"].major_ticks.set_ticks([])

    majortick_iter, minortick_iter = ax1.axis[
        'bottom']._axis_artist_helper.get_tick_iterators(ax1)
    tick_loc_angle, ticklabel_loc_angle_label = ax1.axis[
        'bottom']._get_tick_info(majortick_iter)

    # ax1.axis["bottom"].toggle(all=False)#, ticks=True)
    ax1.axis["top"].toggle(all=False)  #, ticks=True)
    ax1.axis["left"].toggle(all=False)  #, ticks=True)
    ax1.axis["right"].toggle(all=False)  #, ticks=True)

    aux_ax = ax1.get_aux_axes(tr)

    aux_ax.patch = ax1.patch  # for aux_ax to have a clip path as in ax
    ax1.patch.zorder = 0.9  # but this has a side effect that the patch is
    #  drawn twice, and possibly over some other
    #  artists. So, we decrease the zorder a bit to
    #  prevent this.

    return ax1, aux_ax
Ejemplo n.º 21
0
def curvedEarthAxes(rect=111,
                    fig=None,
                    minground=0.,
                    maxground=2000,
                    minalt=0,
                    maxalt=500,
                    Re=6371.,
                    nyticks=5,
                    nxticks=4):
    """Create curved axes in ground-range and altitude

    Parameters
    ----------
    rect : Optional[int]
        subplot spcification
    fig : Optional[pylab.figure object]
        (default to gcf)
    minground : Optional[float]

    maxground : Optional[int]
        maximum ground range [km]
    minalt : Optional[int]
        lowest altitude limit [km]
    maxalt : Optional[int]
        highest altitude limit [km]
    Re : Optional[float] 
        Earth radius in kilometers
    nyticks : Optional[int]
        Number of y axis tick marks; default is 5
    nxticks : Optional[int]
        Number of x axis tick marks; deafult is 4

    Returns
    -------
    ax : matplotlib.axes object
        containing formatting
    aax : matplotlib.axes object
        containing data

    Example
    -------
        import numpy as np
        from utils import plotUtils
        ax, aax = plotUtils.curvedEarthAxes()
        th = np.linspace(0, ax.maxground/ax.Re, 50)
        r = np.linspace(ax.Re+ax.minalt, ax.Re+ax.maxalt, 20)
        Z = exp( -(r - 300 - ax.Re)**2 / 100**2 ) * np.cos(th[:, np.newaxis]/th.max()*4*np.pi)
        x, y = np.meshgrid(th, r)
        im = aax.pcolormesh(x, y, Z.T)
        ax.grid()

    written by Sebastien, 2013-04

    """
    from matplotlib.transforms import Affine2D, Transform
    import mpl_toolkits.axisartist.floating_axes as floating_axes
    from matplotlib.projections import polar
    from mpl_toolkits.axisartist.grid_finder import FixedLocator, DictFormatter
    import numpy as np
    from pylab import gcf

    ang = maxground / Re
    minang = minground / Re
    angran = ang - minang
    angle_ticks = [(0, "{:.0f}".format(minground))]
    while angle_ticks[-1][0] < angran:
        tang = angle_ticks[-1][0] + 1. / nxticks * angran
        angle_ticks.append((tang, "{:.0f}".format((tang - minang) * Re)))
    grid_locator1 = FixedLocator([v for v, s in angle_ticks])
    tick_formatter1 = DictFormatter(dict(angle_ticks))

    altran = float(maxalt - minalt)
    alt_ticks = [(minalt + Re, "{:.0f}".format(minalt))]
    while alt_ticks[-1][0] < Re + maxalt:
        alt_ticks.append(
            (altran / float(nyticks) + alt_ticks[-1][0],
             "{:.0f}".format(altran / float(nyticks) + alt_ticks[-1][0] - Re)))
    _ = alt_ticks.pop()
    grid_locator2 = FixedLocator([v for v, s in alt_ticks])
    tick_formatter2 = DictFormatter(dict(alt_ticks))

    tr_rotate = Affine2D().rotate(np.pi / 2 - ang / 2)
    tr_shift = Affine2D().translate(0, Re)
    tr = polar.PolarTransform() + tr_rotate

    grid_helper = \
        floating_axes.GridHelperCurveLinear(tr, extremes=(0, angran, Re+minalt,
                                                          Re+maxalt),
                                            grid_locator1=grid_locator1,
                                            grid_locator2=grid_locator2,
                                            tick_formatter1=tick_formatter1,
                                            tick_formatter2=tick_formatter2,)

    if not fig: fig = gcf()
    ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)

    # adjust axis
    ax1.axis["left"].label.set_text(r"Alt. [km]")
    ax1.axis["bottom"].label.set_text(r"Ground range [km]")
    ax1.invert_xaxis()

    ax1.minground = minground
    ax1.maxground = maxground
    ax1.minalt = minalt
    ax1.maxalt = maxalt
    ax1.Re = Re

    fig.add_subplot(ax1, transform=tr)

    # create a parasite axes whose transData in RA, cz
    aux_ax = ax1.get_aux_axes(tr)

    # for aux_ax to have a clip path as in ax
    aux_ax.patch = ax1.patch

    # but this has a side effect that the patch is drawn twice, and possibly
    # over some other artists. So, we decrease the zorder a bit to prevent this.
    ax1.patch.zorder = 0.9

    return ax1, aux_ax
Ejemplo n.º 22
0
def draw_rink(is_horizontal=True, x_range=None, y_range=None, rink_length=None):
    """
    Draw a plot of an NHL ice surface.

    Plotting is based on the NHL coordinate system which goes from -100 to 100 on the x-axis and -42.5 to 42.5
    on the y-axis.

    Args:
        is_horizontal: bool; default=True
            Indicates whether to draw the rink horizontally (left and right as end boards) or vertically (top and
            bottom as end boards).

            When is_horizontal is False, x,y-coordinates will have to be reversed (x,y => y,x) when adding to the plot.

        x_range: "half", "ozone", float, or list; default=None
            Lower and upper bounds of the default display on the x-axis.  The entire rink will be drawn regardless,
            this only controls what section of the rink's length is initially shown.

            Coordinates can range from -100 to 100.

            "half": bounds set to 0 and 100.
            "ozone": bounds set to 25 and 100.
            float: value will be used as the lower bound with 100 as the upper bound.
            list: will attempt to use the first element as the lower bound, the second as the upper bound.

            If none of the above are provided, the bounds will be set to -100 and 100.

        y_range: "half", float or list; default=None
            Lower and upper bounds of the default display on the y-axis.  The entire rink will be drawn regardless,
            this only controls what section of the rink's width is initially shown.

            Coordinates can range from -42.5 to 42.5.

            "half": bounds set to 0 and 42.5.
            float: value will be used as the lower bound with 42.5 as the upper bound.
            list: will attempt to use the first element as the lower bound, the second as the upper bound.

            If none of the above are provided, the bounds will bet set to -42.5 and 42.5.

        rink_length: float; default=None
            Length of the rink (end board to end board) for plotting.

            If None, will use default of 14 if using a full horizontal rink or 8 otherwise.
            Width is set automatically based on rink_length and dimensions used.

    Returns:
        matplotlib Axes.
            Axes for the rink plot.
    """

    if x_range in ("half", "ozone"):
        x_range = [0 if x_range == "half" else 25, 100]
    elif isinstance(x_range, (int, float)):
        x_range = [x_range, 100]
    elif isinstance(x_range, list) and x_range:
        x_range.append(100)
        x_range = x_range[:2]
    else:
        x_range = [-100, 100]

    if x_range[0] > x_range[1]:
        x_range = x_range[::-1]

    x_range = [max(-100, x_range[0]), min(100, x_range[1])]

    if x_range[0] >= 100:
        x_range = [-100, 100]

    if y_range == "half":
        y_range = [0, 42.5]
    elif isinstance(y_range, (int, float)):
        y_range = [y_range, 42.5]
    elif isinstance(y_range, list) and y_range:
        y_range.append(42.5)
        y_range = y_range[:2]
    else:
        y_range = [-42.5, 42.5]

    if y_range[0] > y_range[1]:
        y_range = y_range[::-1]

    y_range = [max(-42.5, y_range[0]), min(100, y_range[1])]

    if y_range[0] >= 42.5:
        y_range = [-42.5, 42.5]

    delta_x = x_range[1] - x_range[0]
    delta_y = y_range[1] - y_range[0]
    if rink_length is None:
        rink_length = 14 if is_horizontal and delta_x == 200 else 8

    length = rink_length
    width = rink_length * delta_y / delta_x
    if not is_horizontal:
        length, width = width, length
    plt.figure(figsize=(length, width))

    ax = plt.gca()
    ax.set_aspect("equal")

    patches = []

    # red line
    # 1' in width
    # all other red lines are 2" in width, but will be drawn thicker
    patches.append(mpatches.Rectangle((-0.5, -42.5), 1, 85, color="red", zorder=0))

    # center faceoff
    patches.append(mpatches.Circle((0, 0), .5, color="blue", fill=True, zorder=0))

    # center circle
    patches.append(mpatches.Circle((0, 0), 15, color="red", fill=False, zorder=0))

    # ref half-circle
    # 10' radius
    patches.append(mpatches.Arc((0, -42.5), 20, 20, theta1=0, theta2=180, color="red", zorder=0))

    # 5'7" between, 5'7" = 67", divide by 12 to get feet, divide by 2 to get half on each side
    between_hashmarks = 67 / 24

    # edge of the circle
    hashmark_edge = (15**2 - between_hashmarks**2)**.5

    for side in (-1, 1):
        # blue lines
        # 1' in width
        # neutral zone is 50', half on each side = 25
        patches.append(mpatches.Rectangle((25 * side, -42.5), side, 85, color="blue", zorder=0))

        for y in (-22, 22):
            # faceoff dots
            # 2' diameter
            # offensive zone dots are 20' from the goal line with 44' in between
            patches.append(mpatches.Circle((69 * side, y), 1, color="red", fill=True, zorder=0))

            # neutral zone dots
            # 5' from the bluelines (25 - 5 = 20), 44' in between
            patches.append(mpatches.Circle((20 * side, y), 1, color="red", fill=True, zorder=0))

            # faceoff circles
            # 15' radius
            patches.append(mpatches.Circle((69 * side, y), 15, color="red", fill=False, zorder=0))

            for circle_side in (-1, 1):
                # faceoff lines
                # 4' length, 3' width
                ax.plot((69 * side * circle_side + 2 * side, 69 * circle_side * side + 6 * side), (y + 1.75, y + 1.75),
                        "red", zorder=0)
                ax.plot((69 * side * circle_side + 2 * side, 69 * circle_side * side + 6 * side), (y - 1.75, y - 1.75),
                        "red", zorder=0)
                ax.plot((69 * side * circle_side + 2 * side, 69 * circle_side * side + 2 * side), (y + 1.75, y + 4.75),
                        "red", zorder=0)
                ax.plot((69 * side * circle_side + 2 * side, 69 * circle_side * side + 2 * side), (y - 1.75, y - 4.75),
                        "red", zorder=0)

                # hashmarks
                ax.plot((69 * side * circle_side + between_hashmarks * side,
                         69 * side * circle_side + between_hashmarks * side),
                        (y - hashmark_edge, y - hashmark_edge - 2),
                        "red", zorder=0)
                ax.plot((69 * side * circle_side + between_hashmarks * side,
                         69 * side * circle_side + between_hashmarks * side),
                        (y + hashmark_edge, y + hashmark_edge + 2),
                        "red", zorder=0)

        # nets
        # depth is 40" with 18" radius (NHL rulebook says 20", but supposed to be 18")
        # width of posts is 19/8", total width is 88"
        # almost certainly incorrect, but close enough
        patches.append(mpatches.Circle(((89 + 11/6) * side, 3 - 5/6), 18/12, color="grey", zorder=2))
        patches.append(mpatches.Circle(((89 + 11/6) * side, 5/6 - 3), 18/12, color="grey", zorder=2))
        patches.append(mpatches.Polygon(
            [[89 * side, 3 + 19 / 8 / 12], [(89 + 11/6) * side, 88 / 24], [(89 + 40 / 12) * side, 3 - 5/6],
             [(89 + 40 / 12) * side, 5 / 6 - 3], [(89 + 11 / 6) * side, -88 / 24], [89 * side, -3 - 19 / 8 / 12]],
            color="grey", zorder=2))

        # creases
        # rectangle extends 4'6" out, then ellipse of width 2'
        patches.append(mpatches.Rectangle((89*side, -4), 4.5*-side, 8, color="lightblue", zorder=-1))
        patches.append(arc_patch((84.5*side, 0), 2, 4, 270-180*side, 270, fill=True, color="lightblue", zorder=-1))

        # outline of crease
        ax.plot((89*side, 84.5*side), (-4, -4), "red", zorder=1)
        ax.plot((89*side, 84.5*side), (4, 4), "red", zorder=1)
        patches.append(mpatches.Arc((84.5*side, 0), 4, 8, theta1=90*side, theta2=270*side, color="red", zorder=1))

        # restricted zone
        # 8' from the post to 11' from the post (posts are 3' from center)
        ax.plot((89*side, 100*side), (-11, -14), "red", zorder=0)
        ax.plot((89*side, 100*side), (11, 14), "red", zorder=0)

        # curves at end of boards
        # arc of a circle with 28' radius
        patches.append(mpatches.Arc(((100 - 28) * side, 42.5 - 28), 56, 56,
                                    theta1=45 - 45 * side, theta2=135 - 45 * side,
                                    color="black", zorder=2))
        patches.append(mpatches.Arc(((100 - 28) * side, -42.5 + 28), 56, 56,
                                    theta1=225 + 45 * side, theta2=135 - 135 * side,
                                    color="black", zorder=2))

        # side boards
        ax.plot((100 - 28, 28 - 100), (42.5 * side, 42.5 * side), "black", zorder=2)
        # end boards
        ax.plot((100 * side, 100 * side), (42.5 - 28, 28 - 42.5), "black", zorder=2)

        # goal lines
        # 11' from end boards
        # boards curl in arc of circle with 28' radius
        # distance from center of circle to side boards = 28'
        # distance from center of circle to goal line = 28' - 11'
        # distance from center of ice (on y-axis) to center of circle = 42.5 - 28
        end_board_y = np.sqrt(28 ** 2 - (28 - 11) ** 2) + (42.5 - 28)
        ax.plot((89 * side, 89 * side), (-end_board_y, end_board_y), "red", zorder=1)

    ax.tick_params(axis="both", which="both", bottom=False, left=False, labelbottom=False, labelleft=False)

    # rotate everything 90 degrees if displaying rink vertically
    trans = Affine2D().rotate_deg(0 if is_horizontal else 90) + ax.transData

    for patch in patches:
        patch.set_transform(trans)
        ax.add_patch(patch)

    for line in ax.lines:
        line.set_transform(trans)

    if not is_horizontal:
        old = ax.axis()
        ax.axis(old[2:4] + old[0:2])
        x_range, y_range = y_range, x_range

    # only display the specified region of the rink
    ax.set_xlim(*x_range)
    ax.set_ylim(*y_range)

    if not is_horizontal:
        ax.invert_yaxis()

    return ax
Ejemplo n.º 23
0
def test_axis_direction():
    fig = plt.figure(figsize=(5, 5))

    # PolarAxes.PolarTransform takes radian. However, we want our coordinate
    # system in degree
    tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform()

    # polar projection, which involves cycle, and also has limits in
    # its coordinates, needs a special method to find the extremes
    # (min, max of the coordinate within the view).

    # 20, 20 : number of sampling points along x, y direction
    extreme_finder = angle_helper.ExtremeFinderCycle(
        20,
        20,
        lon_cycle=360,
        lat_cycle=None,
        lon_minmax=None,
        lat_minmax=(0, np.inf),
    )

    grid_locator1 = angle_helper.LocatorDMS(12)
    tick_formatter1 = angle_helper.FormatterDMS()

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1)

    ax1 = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)

    for axis in ax1.axis.values():
        axis.set_visible(False)

    fig.add_subplot(ax1)

    ax1.axis["lat1"] = axis = grid_helper.new_floating_axis(
        0, 130, axes=ax1, axis_direction="left")
    axis.label.set_text("Test")
    axis.label.set_visible(True)
    axis.get_helper()._extremes = 0.001, 10

    ax1.axis["lat2"] = axis = grid_helper.new_floating_axis(
        0, 50, axes=ax1, axis_direction="right")
    axis.label.set_text("Test")
    axis.label.set_visible(True)
    axis.get_helper()._extremes = 0.001, 10

    ax1.axis["lon"] = axis = grid_helper.new_floating_axis(
        1, 10, axes=ax1, axis_direction="bottom")
    axis.label.set_text("Test 2")
    axis.get_helper()._extremes = 50, 130
    axis.major_ticklabels.set_axis_direction("top")
    axis.label.set_axis_direction("top")

    grid_helper.grid_finder.grid_locator1.den = 5
    grid_helper.grid_finder.grid_locator2._nbins = 5

    ax1.set_aspect(1.)
    ax1.set_xlim(-8, 8)
    ax1.set_ylim(-4, 12)

    ax1.grid(True)
Ejemplo n.º 24
0
    def _make_image(self,
                    A,
                    in_bbox,
                    out_bbox,
                    clip_bbox,
                    magnification=1.0,
                    unsampled=False,
                    round_to_pixel_border=True):
        """
        Normalize, rescale and color the image `A` from the given
        in_bbox (in data space), to the given out_bbox (in pixel
        space) clipped to the given clip_bbox (also in pixel space),
        and magnified by the magnification factor.

        `A` may be a greyscale image (MxN) with a dtype of `float32`,
        `float64`, `uint16` or `uint8`, or an RGBA image (MxNx4) with
        a dtype of `float32`, `float64`, or `uint8`.

        If `unsampled` is True, the image will not be scaled, but an
        appropriate affine transformation will be returned instead.

        If `round_to_pixel_border` is True, the output image size will
        be rounded to the nearest pixel boundary.  This makes the
        images align correctly with the axes.  It should not be used
        in cases where you want exact scaling, however, such as
        FigureImage.

        Returns the resulting (image, x, y, trans), where (x, y) is
        the upper left corner of the result in pixel space, and
        `trans` is the affine transformation from the image to pixel
        space.
        """
        if A is None:
            raise RuntimeError('You must first set the image'
                               ' array or the image attribute')

        clipped_bbox = Bbox.intersection(out_bbox, clip_bbox)

        if clipped_bbox is None:
            return None, 0, 0, None

        out_width_base = clipped_bbox.width * magnification
        out_height_base = clipped_bbox.height * magnification

        if out_width_base == 0 or out_height_base == 0:
            return None, 0, 0, None

        if self.origin == 'upper':
            # Flip the input image using a transform.  This avoids the
            # problem with flipping the array, which results in a copy
            # when it is converted to contiguous in the C wrapper
            t0 = Affine2D().translate(0, -A.shape[0]).scale(1, -1)
        else:
            t0 = IdentityTransform()

        t0 += (Affine2D().scale(in_bbox.width / A.shape[1], in_bbox.height /
                                A.shape[0]).translate(in_bbox.x0, in_bbox.y0) +
               self.get_transform())

        t = (t0 +
             Affine2D().translate(-clipped_bbox.x0, -clipped_bbox.y0).scale(
                 magnification, magnification))

        # So that the image is aligned with the edge of the axes, we want
        # to round up the output width to the next integer.  This also
        # means scaling the transform just slightly to account for the
        # extra subpixel.
        if (t.is_affine and round_to_pixel_border and
            (out_width_base % 1.0 != 0.0 or out_height_base % 1.0 != 0.0)):
            out_width = int(ceil(out_width_base))
            out_height = int(ceil(out_height_base))
            extra_width = (out_width - out_width_base) / out_width_base
            extra_height = (out_height - out_height_base) / out_height_base
            t += Affine2D().scale(1.0 + extra_width, 1.0 + extra_height)
        else:
            out_width = int(out_width_base)
            out_height = int(out_height_base)

        if not unsampled:
            created_rgba_mask = False

            if A.ndim not in (2, 3):
                raise ValueError("Invalid dimensions, got %s" % (A.shape, ))

            if A.ndim == 2:
                A = self.norm(A)
                if A.dtype.kind == 'f':
                    # If the image is greyscale, convert to RGBA and
                    # use the extra channels for resizing the over,
                    # under, and bad pixels.  This is needed because
                    # Agg's resampler is very aggressive about
                    # clipping to [0, 1] and we use out-of-bounds
                    # values to carry the over/under/bad information
                    rgba = np.empty((A.shape[0], A.shape[1], 4), dtype=A.dtype)
                    rgba[..., 0] = A  # normalized data
                    rgba[..., 1] = A < 0  # under data
                    rgba[..., 2] = A > 1  # over data
                    rgba[..., 3] = ~A.mask  # bad data
                    A = rgba
                    output = np.zeros((out_height, out_width, 4),
                                      dtype=A.dtype)
                    alpha = 1.0
                    created_rgba_mask = True
                else:
                    # colormap norms that output integers (ex NoNorm
                    # and BoundaryNorm) to RGBA space before
                    # interpolating.  This is needed due to the
                    # Agg resampler only working on floats in the
                    # range [0, 1] and because interpolating indexes
                    # into an arbitrary LUT may be problematic.
                    #
                    # This falls back to interpolating in RGBA space which
                    # can produce it's own artifacts of colors not in the map
                    # showing up in the final image.
                    A = self.cmap(A, alpha=self.get_alpha(), bytes=True)

            if not created_rgba_mask:
                # Always convert to RGBA, even if only RGB input
                if A.shape[2] == 3:
                    A = _rgb_to_rgba(A)
                elif A.shape[2] != 4:
                    raise ValueError("Invalid dimensions, got %s" %
                                     (A.shape, ))

                output = np.zeros((out_height, out_width, 4), dtype=A.dtype)

                alpha = self.get_alpha()
                if alpha is None:
                    alpha = 1.0

            _image.resample(A, output, t, _interpd_[self.get_interpolation()],
                            self.get_resample(), alpha,
                            self.get_filternorm() or 0.0,
                            self.get_filterrad() or 0.0)

            if created_rgba_mask:
                # Convert back to a masked greyscale array so
                # colormapping works correctly
                hid_output = output
                output = np.ma.masked_array(hid_output[..., 0],
                                            hid_output[..., 3] < 0.5)
                # relabel under data
                output[hid_output[..., 1] > .5] = -1
                # relabel over data
                output[hid_output[..., 2] > .5] = 2

            output = self.to_rgba(output, bytes=True, norm=False)

            # Apply alpha *after* if the input was greyscale without a mask
            if A.ndim == 2 or created_rgba_mask:
                alpha = self.get_alpha()
                if alpha is not None and alpha != 1.0:
                    alpha_channel = output[:, :, 3]
                    alpha_channel[:] = np.asarray(
                        np.asarray(alpha_channel, np.float32) * alpha,
                        np.uint8)
        else:
            if self._imcache is None:
                self._imcache = self.to_rgba(A, bytes=True, norm=(A.ndim == 2))
            output = self._imcache

            # Subset the input image to only the part that will be
            # displayed
            subset = TransformedBbox(clip_bbox,
                                     t0.frozen().inverted()).frozen()
            output = output[int(max(subset.ymin, 0)
                                ):int(min(subset.ymax + 1, output.shape[0])),
                            int(max(subset.xmin, 0)
                                ):int(min(subset.xmax + 1, output.shape[1]))]

            t = Affine2D().translate(int(max(subset.xmin, 0)),
                                     int(max(subset.ymin, 0))) + t

        return output, clipped_bbox.x0, clipped_bbox.y0, t
Ejemplo n.º 25
0
    def _label(self, colors):
        """
        Method to create an edges label

        :return:
        """

        its_label = '{:g}'.format(
            abs(self.edge.capacity) if self.edge.capacity is not None else '')
        status_color = colors['bc']
        if not self.layout_mode:
            in_percent = int(
                round(100 * self.edge.flux / float(self.edge.capacity), 0))
            if in_percent < 100:
                status_color = colors['wc']
            its_label += '; {:g}%'.format(in_percent)
        # get the label coordinates based on self.segments (use the end of the first segment if several segments are
        # present, if it is a single, place the label half the way

        s_coords, e_coords = self.segments[0]
        edge_vec = Vec(s_coords, e_coords)
        start_vec = Vec(s_coords)
        # maybe always use the last part...
        if len(self.segments) == 1:  # single segment: place it half way
            part_way_vec = start_vec + self.label_position * edge_vec
            coords_label = part_way_vec.coords[:2]
        else:  # several segments: place label at the end of the first
            #half_way_vec = start_vec + 0.5 * edge_vec
            #coords_label = half_way_vec.coords[:2]
            coords_label = e_coords
        if self.with_label:
            self.labels = [
                # so far there is just a single label for an edge
                (its_label, coords_label, {
                    'horizontalalignment': 'center',
                    'verticalalignment': 'center',
                    'size': self.scale * 200 * self.label_scale,
                    'color': colors['tc'],
                    'box_fc': status_color
                })
            ]
        else:
            self.labels = None
        # cross through the label if it is not functional
        if not self.functional:
            cross_bar = FancyBboxPatch(
                (0, 0),
                self.scale * self.label_scale,
                0.07 * self.scale * self.label_scale,
                boxstyle="square,pad={}".format(0.05 * self.scale *
                                                self.label_scale),
                color=colors['ac'],
                alpha=min(1, 2 * self.alpha),
                zorder=6)
            rotator = Affine2D().rotate_deg(-45)
            corner_coords = (coords_label[0] -
                             0.4 * self.scale * self.label_scale,
                             coords_label[1] +
                             0.3 * self.scale * self.label_scale)
            translator = Affine2D().translate(*corner_coords)
            transform = rotator + translator
            cross_bar.set_transform(transform)
            self.super_patch_collection.append(cross_bar)
Ejemplo n.º 26
0
def plot_day_summary2_ohlc(ax, opens, highs, lows, closes, ticksize=4,
                           colorup='k', colordown='r'):
    """Represent the time, open, high, low, close as a vertical line
    ranging from low to high.  The left tick is the open and the right
    tick is the close.
    *opens*, *highs*, *lows* and *closes* must have the same length.
    NOTE: this code assumes if any value open, high, low, close is
    missing (*-1*) they all are missing
    Parameters
    ----------
    ax : `Axes`
        an Axes instance to plot to
    opens : sequence
        sequence of opening values
    highs : sequence
        sequence of high values
    lows : sequence
        sequence of low values
    closes : sequence
        sequence of closing values
    ticksize : int
        size of open and close ticks in points
    colorup : color
        the color of the lines where close >= open
    colordown : color
         the color of the lines where close <  open
    Returns
    -------
    ret : list
        a list of lines added to the axes
    """

    _check_input(opens, highs, lows, closes)

    rangeSegments = [((i, low), (i, high)) for i, low, high in
                     zip(xrange(len(lows)), lows, highs) if low != -1]

    # the ticks will be from ticksize to 0 in points at the origin and
    # we'll translate these to the i, close location
    openSegments = [((-ticksize, 0), (0, 0))]

    # the ticks will be from 0 to ticksize in points at the origin and
    # we'll translate these to the i, close location
    closeSegments = [((0, 0), (ticksize, 0))]

    offsetsOpen = [(i, open) for i, open in
                   zip(xrange(len(opens)), opens) if open != -1]

    offsetsClose = [(i, close) for i, close in
                    zip(xrange(len(closes)), closes) if close != -1]

    scale = ax.figure.dpi * (1.0 / 72.0)

    tickTransform = Affine2D().scale(scale, 0.0)

    colorup = mcolors.to_rgba(colorup)
    colordown = mcolors.to_rgba(colordown)
    colord = {True: colorup, False: colordown}
    colors = [colord[open < close] for open, close in
              zip(opens, closes) if open != -1 and close != -1]

    useAA = 0,   # use tuple here
    lw = 1,      # and here
    rangeCollection = LineCollection(rangeSegments,
                                     colors=colors,
                                     linewidths=lw,
                                     antialiaseds=useAA,
                                     )

    openCollection = LineCollection(openSegments,
                                    colors=colors,
                                    antialiaseds=useAA,
                                    linewidths=lw,
                                    offsets=offsetsOpen,
                                    transOffset=ax.transData,
                                    )
    openCollection.set_transform(tickTransform)

    closeCollection = LineCollection(closeSegments,
                                     colors=colors,
                                     antialiaseds=useAA,
                                     linewidths=lw,
                                     offsets=offsetsClose,
                                     transOffset=ax.transData,
                                     )
    closeCollection.set_transform(tickTransform)

    minpy, maxx = (0, len(rangeSegments))
    miny = min([low for low in lows if low != -1])
    maxy = max([high for high in highs if high != -1])
    corners = (minpy, miny), (maxx, maxy)
    ax.update_datalim(corners)
    ax.autoscale_view()

    # add these last
    ax.add_collection(rangeCollection)
    ax.add_collection(openCollection)
    ax.add_collection(closeCollection)
    return rangeCollection, openCollection, closeCollection
Ejemplo n.º 27
0
 def _basic_outline(self, colors):
     if self.need != 0:
         #center = Circle(
         #    self.coords,
         #    self.r / float(20),
         #    color=color['lc'], zorder=4, facecolor=color['lc'], fill=True, alpha=self.alpha
         #)
         if self.layout_mode:
             border = Circle(self.coords,
                             0.99 * self.r,
                             color=colors['lc'],
                             zorder=3,
                             fill=False,
                             edgecolor=colors['lc'],
                             alpha=self.alpha,
                             linewidth=70 * self.scale)
             role_fill = Circle(self.coords,
                                self.r,
                                color=self.role_color,
                                zorder=4,
                                facecolor=self.role_color,
                                fill=True,
                                alpha=0.5 * self.alpha)
             self.basic_structure = [role_fill, border]
         else:
             border = Circle(self.coords,
                             self.r,
                             color=colors['lc'],
                             zorder=3,
                             fill=False,
                             edgecolor=colors['lc'],
                             alpha=self.alpha,
                             linewidth=0.5)
             self.basic_structure = [
                 border,
             ]  #center]
     else:
         center = Circle(self.coords,
                         self.r / self._transit_fraction,
                         color=colors['lc'],
                         zorder=4,
                         facecolor=colors['lc'],
                         fill=True,
                         alpha=self.alpha)
         self.basic_structure = [center]
     if not self.functional:
         size = self.r if self.need != 0. else self.r / self._transit_fraction
         cross_bar = FancyBboxPatch(
             (0, 0),
             2.4 * size,
             0.2 * size,
             boxstyle="square,pad={}".format(0.05 * self.scale *
                                             self.label_scale),
             color=colors['ac'],
             alpha=min(1, 2 * self.alpha),
             zorder=6)
         rotator = Affine2D().rotate_deg(-40)
         corner_coords = (self.coords[0] - size,
                          self.coords[1] + 0.7 * size)
         translator = Affine2D().translate(*corner_coords)
         transform = rotator + translator
         cross_bar.set_transform(transform)
         self.super_patch_collection.append(cross_bar)
Ejemplo n.º 28
0
def plot_polar_heatmap(data,
                       name,
                       interp_factor=5.,
                       color_limits=False,
                       hide_colorbar=False,
                       vmin=None,
                       vmax=None,
                       log_scale=True,
                       dpi=200,
                       output_dir=None):
    """Plots the polar heatmap describing azimuth and latitude / elevation components.

    Plots the polar heatmap where each cell of the heatmap corresponds to
    the specific element of the array provided by `gather_polar_errors`
    function.

    Parameters
    ----------
    data : 2D array
        Indicates the array containing the sum of angular errors within the
        specified angular ranges. It is usually provided by `gather_polar_errors`
        function.

    name : str
        Indicates the name of the output png file.

    interp_factor : float
        Indicates the interpolation factor of the heatmap.

    color_limits : boolean
        Specifies if the determined intensity limits should be returned.

    hide_colorbar : boolean
        Specifies if the colorbar should be hidden.

    vmin : float
        Indicates the minimum value of the colorbar.

    vmax : float
        Indicates the maximum value of the colorbar.

    log_scale : float
        Specifies if the heatmap sould be in the logarithmic scale.

    dpi : integer
        Indicates the DPI of the output image.

    output_dir : str
        Indicates the path to the output folder where the image will be stored.
    """
    th0, th1 = 0., 180.
    r0, r1 = 0, 90
    thlabel, rlabel = 'Azimuth', 'Elevation'

    tr_scale = Affine2D().scale(np.pi / 180., 1.)
    tr = tr_scale + PolarAxes.PolarTransform()

    lat_ticks = [(.0 * 90., '0$^{\circ}$'), (.33 * 90., '30$^{\circ}$'),
                 (.66 * 90., '60$^{\circ}$'), (1. * 90., '90$^{\circ}$')]
    r_grid_locator = FixedLocator([v for v, s in lat_ticks])
    r_grid_formatter = DictFormatter(dict(lat_ticks))

    angle_ticks = [(0 * 180., '90$^{\circ}$'), (.25 * 180., '45$^{\circ}$'),
                   (.5 * 180., '0$^{\circ}$'), (.75 * 180., '-45$^{\circ}$'),
                   (1. * 180., '-90$^{\circ}$')]
    theta_grid_locator = FixedLocator([v for v, s in angle_ticks])
    theta_tick_formatter = DictFormatter(dict(angle_ticks))

    grid_helper = GridHelperCurveLinear(tr,
                                        extremes=(th0, th1, r0, r1),
                                        grid_locator1=theta_grid_locator,
                                        grid_locator2=r_grid_locator,
                                        tick_formatter1=theta_tick_formatter,
                                        tick_formatter2=r_grid_formatter)

    fig = plt.figure()
    ax = floating_axes.FloatingSubplot(fig, 111, grid_helper=grid_helper)
    fig.add_subplot(ax)
    ax.set_facecolor('white')

    ax.axis["bottom"].set_visible(False)
    ax.axis["top"].toggle(ticklabels=True, label=True)
    ax.axis["top"].set_axis_direction("bottom")
    ax.axis["top"].major_ticklabels.set_axis_direction("top")
    ax.axis["top"].label.set_axis_direction("top")

    ax.axis["left"].set_axis_direction("bottom")
    ax.axis["right"].set_axis_direction("top")

    ax.axis["top"].label.set_text(thlabel)
    ax.axis["left"].label.set_text(rlabel)

    aux_ax = ax.get_aux_axes(tr)
    aux_ax.patch = ax.patch
    ax.patch.zorder = 0.9

    rad = np.linspace(0, 90, data.shape[1])
    azm = np.linspace(0, 180, data.shape[0])

    f = interpolate.interp2d(rad,
                             azm,
                             data,
                             kind='linear',
                             bounds_error=True,
                             fill_value=0)

    new_rad = np.linspace(0, 90, 180 * interp_factor)
    new_azm = np.linspace(0, 180, 360 * interp_factor)
    new_data_angle_dist = f(new_rad, new_azm)
    new_r, new_th = np.meshgrid(new_rad, new_azm)
    new_data_angle_dist += 1.

    if log_scale:
        data_mesh = aux_ax.pcolormesh(
            new_th,
            new_r,
            new_data_angle_dist,
            cmap='jet',
            norm=colors.LogNorm(
                vmin=1. if vmin is None else vmin,
                vmax=new_data_angle_dist.max() if vmax is None else vmax))
    else:
        data_mesh = aux_ax.pcolormesh(new_th,
                                      new_r,
                                      new_data_angle_dist,
                                      cmap='jet',
                                      vmin=vmin,
                                      vmax=vmax)

    cbar = plt.colorbar(data_mesh,
                        orientation='vertical',
                        shrink=.88,
                        pad=.1,
                        aspect=15)
    cbar.ax.set_ylabel('Absolute error, [deg.]')

    if hide_colorbar:
        cbar.remove()

    ax.grid(False)

    plt.show()

    if output_dir is not None:
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

        fig.savefig(os.path.join(output_dir, '{}_chart.png'.format(name)),
                    transparent=False,
                    bbox_inches='tight',
                    pad_inches=0.1,
                    dpi=dpi)

    if color_limits:
        return 1., new_data_angle_dist.max()
Ejemplo n.º 29
0
 def _get_transformed_clip_path(self):
     x, y, width, height = self.state.clipping_path
     rect = ((x, y), (x+width, y), (x+width, y+height), (x, y+height))
     transform = Affine2D.from_values(*affine.affine_params(self.get_ctm()))
     return TransformedPath(Path(rect), transform)
Ejemplo n.º 30
0
def curvelinear_test3(fig):
    """
    polar projection, but in a rectangular box.
    """
    global ax1, axis
    import numpy as np
    import angle_helper
    from matplotlib.projections import PolarAxes

    # PolarAxes.PolarTransform takes radian. However, we want our coordinate
    # system in degree
    tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform()

    # polar projection, which involves cycle, and also has limits in
    # its coordinates, needs a special method to find the extremes
    # (min, max of the coordinate within the view).

    grid_locator1 = angle_helper.LocatorDMS(15)
    # Find a grid values appropriate for the coordinate (degree,
    # minute, second).

    tick_formatter1 = angle_helper.FormatterDMS()
    # And also uses an appropriate formatter.  Note that,the
    # acceptable Locator and Formatter class is a bit different than
    # that of mpl's, and you cannot directly use mpl's Locator and
    # Formatter here (but may be possible in the future).

    from grid_finder import FixedLocator
    grid_locator2 = FixedLocator([2, 4, 6, 8, 10])

    grid_helper = GridHelperCurveLinear(
        tr,
        extremes=(0, 360, 10, 3),
        grid_locator1=grid_locator1,
        grid_locator2=grid_locator2,
        tick_formatter1=tick_formatter1,
        tick_formatter2=None,
    )

    ax1 = FloatingSubplot(fig, 111, grid_helper=grid_helper)

    #ax1.axis["top"].set_visible(False)
    #ax1.axis["bottom"].major_ticklabels.set_axis_direction("top")

    fig.add_subplot(ax1)

    #ax1.grid(True)

    r_scale = 10.
    tr2 = Affine2D().scale(1., 1. / r_scale) + tr
    grid_locator2 = FixedLocator([30, 60, 90])
    grid_helper2 = GridHelperCurveLinear(
        tr2,
        extremes=(0, 360, 10. * r_scale, 3. * r_scale),
        grid_locator2=grid_locator2,
    )

    ax1.axis["right"] = axis = grid_helper2.new_fixed_axis("right", axes=ax1)

    ax1.axis["left"].label.set_text("Test 1")
    ax1.axis["right"].label.set_text("Test 2")

    for an in ["left", "right"]:
        ax1.axis[an].set_visible(False)

    #grid_helper2 = ax1.get_grid_helper()
    ax1.axis["z"] = axis = grid_helper.new_floating_axis(
        1, 7, axes=ax1, axis_direction="bottom")
    axis.toggle(all=True, label=True)
    #axis.label.set_axis_direction("top")
    axis.label.set_text("z = ?")
    axis.label.set_visible(True)
    axis.line.set_color("0.5")
    #axis.label.set_visible(True)

    ax2 = ax1.get_aux_axes(tr)

    xx, yy = [67, 90, 75, 30], [2, 5, 8, 4]
    ax2.scatter(xx, yy)
    l, = ax2.plot(xx, yy, "k-")
    l.set_clip_path(ax1.patch)
Ejemplo n.º 31
0
def curvelinear_test2(fig):
    """
    Polar projection, but in a rectangular box.
    """

    # PolarAxes.PolarTransform takes radian. However, we want our coordinate
    # system in degree
    tr = Affine2D().scale(np.pi / 180, 1) + PolarAxes.PolarTransform()
    # Polar projection, which involves cycle, and also has limits in
    # its coordinates, needs a special method to find the extremes
    # (min, max of the coordinate within the view).
    extreme_finder = angle_helper.ExtremeFinderCycle(
        nx=20,
        ny=20,  # Number of sampling points in each direction.
        lon_cycle=360,
        lat_cycle=None,
        lon_minmax=None,
        lat_minmax=(0, np.inf),
    )
    # Find grid values appropriate for the coordinate (degree, minute, second).
    grid_locator1 = angle_helper.LocatorDMS(12)
    # Use an appropriate formatter.  Note that the acceptable Locator and
    # Formatter classes are a bit different than that of Matplotlib, which
    # cannot directly be used here (this may be possible in the future).
    tick_formatter1 = angle_helper.FormatterDMS()

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1)
    ax1 = fig.add_subplot(1,
                          2,
                          2,
                          axes_class=HostAxes,
                          grid_helper=grid_helper)

    # make ticklabels of right and top axis visible.
    ax1.axis["right"].major_ticklabels.set_visible(True)
    ax1.axis["top"].major_ticklabels.set_visible(True)
    # let right axis shows ticklabels for 1st coordinate (angle)
    ax1.axis["right"].get_helper().nth_coord_ticks = 0
    # let bottom axis shows ticklabels for 2nd coordinate (radius)
    ax1.axis["bottom"].get_helper().nth_coord_ticks = 1

    ax1.set_aspect(1)
    ax1.set_xlim(-5, 12)
    ax1.set_ylim(-5, 10)

    ax1.grid(True, zorder=0)

    # A parasite axes with given transform
    ax2 = ax1.get_aux_axes(tr)
    # note that ax2.transData == tr + ax1.transData
    # Anything you draw in ax2 will match the ticks and grids of ax1.
    ax1.parasites.append(ax2)
    ax2.plot(np.linspace(0, 30, 51), np.linspace(10, 10, 51), linewidth=2)

    ax2.pcolor(np.linspace(0, 90, 4), np.linspace(0, 10, 4),
               np.arange(9).reshape((3, 3)))
    ax2.contour(np.linspace(0, 90, 4),
                np.linspace(0, 10, 4),
                np.arange(16).reshape((4, 4)),
                colors="k")
Ejemplo n.º 32
0
 def _get_affine_transform(self):
     return Affine2D() \
         .scale(0.25) \
         .translate(0.5, 0.5)
Ejemplo n.º 33
0
def curvelinear_test2(fig):
    """
    polar projection, but in a rectangular box.
    """

    # PolarAxes.PolarTransform takes radian. However, we want our coordinate
    # system in degree
    tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform()

    # polar projection, which involves cycle, and also has limits in
    # its coordinates, needs a special method to find the extremes
    # (min, max of the coordinate within the view).

    # 20, 20 : number of sampling points along x, y direction
    extreme_finder = angle_helper.ExtremeFinderCycle(
        20,
        20,
        lon_cycle=360,
        lat_cycle=None,
        lon_minmax=None,
        lat_minmax=(0, np.inf),
    )

    grid_locator1 = angle_helper.LocatorDMS(12)
    # Find a grid values appropriate for the coordinate (degree,
    # minute, second).

    tick_formatter1 = angle_helper.FormatterDMS()
    # And also uses an appropriate formatter.  Note that,the
    # acceptable Locator and Formatter class is a bit different than
    # that of mpl's, and you cannot directly use mpl's Locator and
    # Formatter here (but may be possible in the future).

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1)

    ax1 = SubplotHost(fig, 1, 2, 2, grid_helper=grid_helper)

    # make ticklabels of right and top axis visible.
    ax1.axis["right"].major_ticklabels.set_visible(True)
    ax1.axis["top"].major_ticklabels.set_visible(True)

    # let right axis shows ticklabels for 1st coordinate (angle)
    ax1.axis["right"].get_helper().nth_coord_ticks = 0
    # let bottom axis shows ticklabels for 2nd coordinate (radius)
    ax1.axis["bottom"].get_helper().nth_coord_ticks = 1

    fig.add_subplot(ax1)

    # A parasite axes with given transform
    ax2 = ParasiteAxesAuxTrans(ax1, tr, "equal")
    # note that ax2.transData == tr + ax1.transData
    # Anthing you draw in ax2 will match the ticks and grids of ax1.
    ax1.parasites.append(ax2)
    intp = cbook.simple_linear_interpolation
    ax2.plot(intp(np.array([0, 30]), 50),
             intp(np.array([10., 10.]), 50),
             linewidth=2.0)

    ax1.set_aspect(1.)
    ax1.set_xlim(-5, 12)
    ax1.set_ylim(-5, 10)

    ax1.grid(True, zorder=0)
Ejemplo n.º 34
0
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes

fig = plt.figure()

plot_extents = 0, 10, 0, 10
transform = Affine2D().rotate_deg(45)
helper = floating_axes.GridHelperCurveLinear(transform, plot_extents)
ax = floating_axes.FloatingSubplot(fig, 111, grid_helper=helper)

fig.add_subplot(ax)
plt.show()
Ejemplo n.º 35
0
    def __init__(self,
                 parent_axes=None,
                 parent_map=None,
                 transform=None,
                 coord_index=None,
                 coord_type='scalar',
                 coord_unit=None,
                 coord_wrap=None,
                 frame=None):

        # Keep a reference to the parent axes and the transform
        self.parent_axes = parent_axes
        self.parent_map = parent_map
        self.transform = transform
        self.coord_index = coord_index
        self.coord_unit = coord_unit
        self.frame = frame

        self.set_coord_type(coord_type, coord_wrap)

        # Initialize ticks
        self.dpi_transform = Affine2D()
        self.offset_transform = ScaledTranslation(0, 0, self.dpi_transform)
        self.ticks = Ticks(transform=parent_axes.transData +
                           self.offset_transform)

        # Initialize tick labels
        self.ticklabels = TickLabels(
            self.frame,
            transform=None,  # display coordinates
            figure=parent_axes.get_figure())
        self.ticks.display_minor_ticks(False)
        self.minor_frequency = 5

        # Initialize axis labels
        self.axislabels = AxisLabels(
            self.frame,
            transform=None,  # display coordinates
            figure=parent_axes.get_figure())

        # Initialize container for the grid lines
        self.grid_lines = []

        # Initialize grid style. Take defaults from matplotlib.rcParams.
        # Based on matplotlib.axis.YTick._get_gridline.
        #
        # Matplotlib's gridlines use Line2D, but ours use PathPatch.
        # Patches take a slightly different format of linestyle argument.
        lines_to_patches_linestyle = {
            '-': 'solid',
            '--': 'dashed',
            '-.': 'dashdot',
            ':': 'dotted',
            'none': 'none',
            'None': 'none',
            ' ': 'none',
            '': 'none'
        }
        self.grid_lines_kwargs = {
            'visible': False,
            'facecolor': 'none',
            'edgecolor': rcParams['grid.color'],
            'linestyle':
            lines_to_patches_linestyle[rcParams['grid.linestyle']],
            'linewidth': rcParams['grid.linewidth'],
            'alpha': rcParams.get('grid.alpha', 1.0),
            'transform': self.parent_axes.transData
        }
Ejemplo n.º 36
0
def test_cursor_data():
    from matplotlib.backend_bases import MouseEvent

    fig, ax = plt.subplots()
    im = ax.imshow(np.arange(100).reshape(10, 10), origin='upper')

    x, y = 4, 4
    xdisp, ydisp = ax.transData.transform([x, y])

    event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
    assert im.get_cursor_data(event) == 44

    # Now try for a point outside the image
    # Tests issue #4957
    x, y = 10.1, 4
    xdisp, ydisp = ax.transData.transform([x, y])

    event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
    assert im.get_cursor_data(event) is None

    # Hmm, something is wrong here... I get 0, not None...
    # But, this works further down in the tests with extents flipped
    # x, y = 0.1, -0.1
    # xdisp, ydisp = ax.transData.transform([x, y])
    # event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
    # z = im.get_cursor_data(event)
    # assert z is None, "Did not get None, got %d" % z

    ax.clear()
    # Now try with the extents flipped.
    im = ax.imshow(np.arange(100).reshape(10, 10), origin='lower')

    x, y = 4, 4
    xdisp, ydisp = ax.transData.transform([x, y])

    event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
    assert im.get_cursor_data(event) == 44

    fig, ax = plt.subplots()
    im = ax.imshow(np.arange(100).reshape(10, 10), extent=[0, 0.5, 0, 0.5])

    x, y = 0.25, 0.25
    xdisp, ydisp = ax.transData.transform([x, y])

    event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
    assert im.get_cursor_data(event) == 55

    # Now try for a point outside the image
    # Tests issue #4957
    x, y = 0.75, 0.25
    xdisp, ydisp = ax.transData.transform([x, y])

    event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
    assert im.get_cursor_data(event) is None

    x, y = 0.01, -0.01
    xdisp, ydisp = ax.transData.transform([x, y])

    event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
    assert im.get_cursor_data(event) is None

    # Now try with additional transform applied to the image artist
    trans = Affine2D().scale(2).rotate(0.5)
    im = ax.imshow(np.arange(100).reshape(10, 10),
                   transform=trans + ax.transData)
    x, y = 3, 10
    xdisp, ydisp = ax.transData.transform([x, y])
    event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
    assert im.get_cursor_data(event) == 44
Ejemplo n.º 37
0
    def add(self, patchlabel='', flows=None, orientations=None, labels='',
            trunklength=1.0, pathlengths=0.25, prior=None, connect=(0, 0),
            rotation=0, **kwargs):
        """
        Add a simple Sankey diagram with flows at the same hierarchical level.

        Return value is the instance of :class:`Sankey`.

        Optional keyword arguments:

          ===============   ===================================================
          Keyword           Description
          ===============   ===================================================
          *patchlabel*      label to be placed at the center of the diagram
                            Note: *label* (not *patchlabel*) will be passed to
                            the patch through ``**kwargs`` and can be used to
                            create an entry in the legend.
          *flows*           array of flow values
                            By convention, inputs are positive and outputs are
                            negative.
          *orientations*    list of orientations of the paths
                            Valid values are 1 (from/to the top), 0 (from/to
                            the left or right), or -1 (from/to the bottom).  If
                            *orientations* == 0, inputs will break in from the
                            left and outputs will break away to the right.
          *labels*          list of specifications of the labels for the flows
                            Each value may be *None* (no labels), '' (just
                            label the quantities), or a labeling string.  If a
                            single value is provided, it will be applied to all
                            flows.  If an entry is a non-empty string, then the
                            quantity for the corresponding flow will be shown
                            below the string.  However, if the *unit* of the
                            main diagram is None, then quantities are never
                            shown, regardless of the value of this argument.
          *trunklength*     length between the bases of the input and output
                            groups
          *pathlengths*     list of lengths of the arrows before break-in or
                            after break-away
                            If a single value is given, then it will be applied
                            to the first (inside) paths on the top and bottom,
                            and the length of all other arrows will be
                            justified accordingly.  The *pathlengths* are not
                            applied to the horizontal inputs and outputs.
          *prior*           index of the prior diagram to which this diagram
                            should be connected
          *connect*         a (prior, this) tuple indexing the flow of the
                            prior diagram and the flow of this diagram which
                            should be connected
                            If this is the first diagram or *prior* is *None*,
                            *connect* will be ignored.
          *rotation*        angle of rotation of the diagram [deg]
                            *rotation* is ignored if this diagram is connected
                            to an existing one (using *prior* and *connect*).
                            The interpretation of the *orientations* argument
                            will be rotated accordingly (e.g., if *rotation*
                            == 90, an *orientations* entry of 1 means to/from
                            the left).
          ===============   ===================================================

        Valid kwargs are :meth:`matplotlib.patches.PathPatch` arguments:

        %(Patch)s

        As examples, ``fill=False`` and ``label='A legend entry'``.
        By default, ``facecolor='#bfd1d4'`` (light blue) and
        ``linewidth=0.5``.

        The indexing parameters (*prior* and *connect*) are zero-based.

        The flows are placed along the top of the diagram from the inside out
        in order of their index within the *flows* list or array.  They are
        placed along the sides of the diagram from the top down and along the
        bottom from the outside in.

        If the sum of the inputs and outputs is nonzero, the discrepancy
        will appear as a cubic Bezier curve along the top and bottom edges of
        the trunk.

        .. seealso::

            :meth:`finish`
        """
        # Check and preprocess the arguments.
        if flows is None:
            flows = np.array([1.0, -1.0])
        else:
            flows = np.array(flows)
        n = flows.shape[0]  # Number of flows
        if rotation is None:
            rotation = 0
        else:
            # In the code below, angles are expressed in deg/90.
            rotation /= 90.0
        if orientations is None:
            orientations = [0, 0]
        if len(orientations) != n:
            raise ValueError(
            "orientations and flows must have the same length.\n"
            "orientations has length %d, but flows has length %d."
            % (len(orientations), n))
        if labels != '' and getattr(labels, '__iter__', False):
            # iterable() isn't used because it would give True if labels is a
            # string
            if len(labels) != n:
                raise ValueError(
                "If labels is a list, then labels and flows must have the "
                "same length.\nlabels has length %d, but flows has length %d."
                % (len(labels), n))
        else:
            labels = [labels] * n
        if trunklength < 0:
            raise ValueError(
            "trunklength is negative.\nThis isn't allowed, because it would "
            "cause poor layout.")
        if np.abs(np.sum(flows)) > self.tolerance:
            _log.info("The sum of the flows is nonzero (%f).\nIs the "
                     "system not at steady state?", np.sum(flows))
        scaled_flows = self.scale * flows
        gain = sum(max(flow, 0) for flow in scaled_flows)
        loss = sum(min(flow, 0) for flow in scaled_flows)
        if not (0.5 <= gain <= 2.0):
            _log.info(
                "The scaled sum of the inputs is %f.\nThis may "
                "cause poor layout.\nConsider changing the scale so"
                " that the scaled sum is approximately 1.0.", gain)
        if not (-2.0 <= loss <= -0.5):
            _log.info(
                "The scaled sum of the outputs is %f.\nThis may "
                "cause poor layout.\nConsider changing the scale so"
                " that the scaled sum is approximately 1.0.", gain)
        if prior is not None:
            if prior < 0:
                raise ValueError("The index of the prior diagram is negative.")
            if min(connect) < 0:
                raise ValueError(
                "At least one of the connection indices is negative.")
            if prior >= len(self.diagrams):
                raise ValueError(
                "The index of the prior diagram is %d, but there are "
                "only %d other diagrams.\nThe index is zero-based."
                % (prior, len(self.diagrams)))
            if connect[0] >= len(self.diagrams[prior].flows):
                raise ValueError(
                "The connection index to the source diagram is %d, but "
                "that diagram has only %d flows.\nThe index is zero-based."
                % (connect[0], len(self.diagrams[prior].flows)))
            if connect[1] >= n:
                raise ValueError(
                "The connection index to this diagram is %d, but this diagram"
                "has only %d flows.\n The index is zero-based."
                % (connect[1], n))
            if self.diagrams[prior].angles[connect[0]] is None:
                raise ValueError(
                "The connection cannot be made.  Check that the magnitude "
                "of flow %d of diagram %d is greater than or equal to the "
                "specified tolerance." % (connect[0], prior))
            flow_error = (self.diagrams[prior].flows[connect[0]] +
                          flows[connect[1]])
            if abs(flow_error) >= self.tolerance:
                raise ValueError(
                "The scaled sum of the connected flows is %f, which is not "
                "within the tolerance (%f)." % (flow_error, self.tolerance))

        # Determine if the flows are inputs.
        are_inputs = [None] * n
        for i, flow in enumerate(flows):
            if flow >= self.tolerance:
                are_inputs[i] = True
            elif flow <= -self.tolerance:
                are_inputs[i] = False
            else:
                _log.info(
                    "The magnitude of flow %d (%f) is below the "
                    "tolerance (%f).\nIt will not be shown, and it "
                    "cannot be used in a connection."
                    % (i, flow, self.tolerance))

        # Determine the angles of the arrows (before rotation).
        angles = [None] * n
        for i, (orient, is_input) in enumerate(zip(orientations, are_inputs)):
            if orient == 1:
                if is_input:
                    angles[i] = DOWN
                elif not is_input:
                    # Be specific since is_input can be None.
                    angles[i] = UP
            elif orient == 0:
                if is_input is not None:
                    angles[i] = RIGHT
            else:
                if orient != -1:
                    raise ValueError(
                    "The value of orientations[%d] is %d, "
                    "but it must be [ -1 | 0 | 1 ]." % (i, orient))
                if is_input:
                    angles[i] = UP
                elif not is_input:
                    angles[i] = DOWN

        # Justify the lengths of the paths.
        if iterable(pathlengths):
            if len(pathlengths) != n:
                raise ValueError(
                "If pathlengths is a list, then pathlengths and flows must "
                "have the same length.\npathlengths has length %d, but flows "
                "has length %d." % (len(pathlengths), n))
        else:  # Make pathlengths into a list.
            urlength = pathlengths
            ullength = pathlengths
            lrlength = pathlengths
            lllength = pathlengths
            d = dict(RIGHT=pathlengths)
            pathlengths = [d.get(angle, 0) for angle in angles]
            # Determine the lengths of the top-side arrows
            # from the middle outwards.
            for i, (angle, is_input, flow) in enumerate(zip(angles, are_inputs,
                                                            scaled_flows)):
                if angle == DOWN and is_input:
                    pathlengths[i] = ullength
                    ullength += flow
                elif angle == UP and not is_input:
                    pathlengths[i] = urlength
                    urlength -= flow  # Flow is negative for outputs.
            # Determine the lengths of the bottom-side arrows
            # from the middle outwards.
            for i, (angle, is_input, flow) in enumerate(reversed(list(zip(
                  angles, are_inputs, scaled_flows)))):
                if angle == UP and is_input:
                    pathlengths[n - i - 1] = lllength
                    lllength += flow
                elif angle == DOWN and not is_input:
                    pathlengths[n - i - 1] = lrlength
                    lrlength -= flow
            # Determine the lengths of the left-side arrows
            # from the bottom upwards.
            has_left_input = False
            for i, (angle, is_input, spec) in enumerate(reversed(list(zip(
                  angles, are_inputs, zip(scaled_flows, pathlengths))))):
                if angle == RIGHT:
                    if is_input:
                        if has_left_input:
                            pathlengths[n - i - 1] = 0
                        else:
                            has_left_input = True
            # Determine the lengths of the right-side arrows
            # from the top downwards.
            has_right_output = False
            for i, (angle, is_input, spec) in enumerate(zip(
                  angles, are_inputs, list(zip(scaled_flows, pathlengths)))):
                if angle == RIGHT:
                    if not is_input:
                        if has_right_output:
                            pathlengths[i] = 0
                        else:
                            has_right_output = True

        # Begin the subpaths, and smooth the transition if the sum of the flows
        # is nonzero.
        urpath = [(Path.MOVETO, [(self.gap - trunklength / 2.0),  # Upper right
                                 gain / 2.0]),
                  (Path.LINETO, [(self.gap - trunklength / 2.0) / 2.0,
                                 gain / 2.0]),
                  (Path.CURVE4, [(self.gap - trunklength / 2.0) / 8.0,
                                 gain / 2.0]),
                  (Path.CURVE4, [(trunklength / 2.0 - self.gap) / 8.0,
                                 -loss / 2.0]),
                  (Path.LINETO, [(trunklength / 2.0 - self.gap) / 2.0,
                                 -loss / 2.0]),
                  (Path.LINETO, [(trunklength / 2.0 - self.gap),
                                 -loss / 2.0])]
        llpath = [(Path.LINETO, [(trunklength / 2.0 - self.gap),  # Lower left
                                 loss / 2.0]),
                  (Path.LINETO, [(trunklength / 2.0 - self.gap) / 2.0,
                                 loss / 2.0]),
                  (Path.CURVE4, [(trunklength / 2.0 - self.gap) / 8.0,
                                 loss / 2.0]),
                  (Path.CURVE4, [(self.gap - trunklength / 2.0) / 8.0,
                                 -gain / 2.0]),
                  (Path.LINETO, [(self.gap - trunklength / 2.0) / 2.0,
                                 -gain / 2.0]),
                  (Path.LINETO, [(self.gap - trunklength / 2.0),
                                 -gain / 2.0])]
        lrpath = [(Path.LINETO, [(trunklength / 2.0 - self.gap),  # Lower right
                                 loss / 2.0])]
        ulpath = [(Path.LINETO, [self.gap - trunklength / 2.0,  # Upper left
                                 gain / 2.0])]

        # Add the subpaths and assign the locations of the tips and labels.
        tips = np.zeros((n, 2))
        label_locations = np.zeros((n, 2))
        # Add the top-side inputs and outputs from the middle outwards.
        for i, (angle, is_input, spec) in enumerate(zip(
              angles, are_inputs, list(zip(scaled_flows, pathlengths)))):
            if angle == DOWN and is_input:
                tips[i, :], label_locations[i, :] = self._add_input(
                    ulpath, angle, *spec)
            elif angle == UP and not is_input:
                tips[i, :], label_locations[i, :] = self._add_output(
                    urpath, angle, *spec)
        # Add the bottom-side inputs and outputs from the middle outwards.
        for i, (angle, is_input, spec) in enumerate(reversed(list(zip(
              angles, are_inputs, list(zip(scaled_flows, pathlengths)))))):
            if angle == UP and is_input:
                tip, label_location = self._add_input(llpath, angle, *spec)
                tips[n - i - 1, :] = tip
                label_locations[n - i - 1, :] = label_location
            elif angle == DOWN and not is_input:
                tip, label_location = self._add_output(lrpath, angle, *spec)
                tips[n - i - 1, :] = tip
                label_locations[n - i - 1, :] = label_location
        # Add the left-side inputs from the bottom upwards.
        has_left_input = False
        for i, (angle, is_input, spec) in enumerate(reversed(list(zip(
              angles, are_inputs, list(zip(scaled_flows, pathlengths)))))):
            if angle == RIGHT and is_input:
                if not has_left_input:
                    # Make sure the lower path extends
                    # at least as far as the upper one.
                    if llpath[-1][1][0] > ulpath[-1][1][0]:
                        llpath.append((Path.LINETO, [ulpath[-1][1][0],
                                                     llpath[-1][1][1]]))
                    has_left_input = True
                tip, label_location = self._add_input(llpath, angle, *spec)
                tips[n - i - 1, :] = tip
                label_locations[n - i - 1, :] = label_location
        # Add the right-side outputs from the top downwards.
        has_right_output = False
        for i, (angle, is_input, spec) in enumerate(zip(
              angles, are_inputs, list(zip(scaled_flows, pathlengths)))):
            if angle == RIGHT and not is_input:
                if not has_right_output:
                    # Make sure the upper path extends
                    # at least as far as the lower one.
                    if urpath[-1][1][0] < lrpath[-1][1][0]:
                        urpath.append((Path.LINETO, [lrpath[-1][1][0],
                                                     urpath[-1][1][1]]))
                    has_right_output = True
                tips[i, :], label_locations[i, :] = self._add_output(
                    urpath, angle, *spec)
        # Trim any hanging vertices.
        if not has_left_input:
            ulpath.pop()
            llpath.pop()
        if not has_right_output:
            lrpath.pop()
            urpath.pop()

        # Concatenate the subpaths in the correct order (clockwise from top).
        path = (urpath + self._revert(lrpath) + llpath + self._revert(ulpath) +
                [(Path.CLOSEPOLY, urpath[0][1])])

        # Create a patch with the Sankey outline.
        codes, vertices = zip(*path)
        vertices = np.array(vertices)

        def _get_angle(a, r):
            if a is None:
                return None
            else:
                return a + r

        if prior is None:
            if rotation != 0:  # By default, none of this is needed.
                angles = [_get_angle(angle, rotation) for angle in angles]
                rotate = Affine2D().rotate_deg(rotation * 90).transform_affine
                tips = rotate(tips)
                label_locations = rotate(label_locations)
                vertices = rotate(vertices)
            text = self.ax.text(0, 0, s=patchlabel, ha='center', va='center')
        else:
            rotation = (self.diagrams[prior].angles[connect[0]] -
                        angles[connect[1]])
            angles = [_get_angle(angle, rotation) for angle in angles]
            rotate = Affine2D().rotate_deg(rotation * 90).transform_affine
            tips = rotate(tips)
            offset = self.diagrams[prior].tips[connect[0]] - tips[connect[1]]
            translate = Affine2D().translate(*offset).transform_affine
            tips = translate(tips)
            label_locations = translate(rotate(label_locations))
            vertices = translate(rotate(vertices))
            kwds = dict(s=patchlabel, ha='center', va='center')
            text = self.ax.text(*offset, **kwds)
        if rcParams['_internal.classic_mode']:
            fc = kwargs.pop('fc', kwargs.pop('facecolor', '#bfd1d4'))
            lw = kwargs.pop('lw', kwargs.pop('linewidth', 0.5))
        else:
            fc = kwargs.pop('fc', kwargs.pop('facecolor', None))
            lw = kwargs.pop('lw', kwargs.pop('linewidth', None))
        if fc is None:
            fc = next(self.ax._get_patches_for_fill.prop_cycler)['color']
        patch = PathPatch(Path(vertices, codes), fc=fc, lw=lw, **kwargs)
        self.ax.add_patch(patch)

        # Add the path labels.
        texts = []
        for number, angle, label, location in zip(flows, angles, labels,
                                                  label_locations):
            if label is None or angle is None:
                label = ''
            elif self.unit is not None:
                quantity = self.format % abs(number) + self.unit
                if label != '':
                    label += "\n"
                label += quantity
            texts.append(self.ax.text(x=location[0], y=location[1],
                                      s=label,
                                      ha='center', va='center'))
        # Text objects are placed even they are empty (as long as the magnitude
        # of the corresponding flow is larger than the tolerance) in case the
        # user wants to provide labels later.

        # Expand the size of the diagram if necessary.
        self.extent = (min(np.min(vertices[:, 0]),
                           np.min(label_locations[:, 0]),
                           self.extent[0]),
                       max(np.max(vertices[:, 0]),
                           np.max(label_locations[:, 0]),
                           self.extent[1]),
                       min(np.min(vertices[:, 1]),
                           np.min(label_locations[:, 1]),
                           self.extent[2]),
                       max(np.max(vertices[:, 1]),
                           np.max(label_locations[:, 1]),
                           self.extent[3]))
        # Include both vertices _and_ label locations in the extents; there are
        # where either could determine the margins (e.g., arrow shoulders).

        # Add this diagram as a subdiagram.
        self.diagrams.append(Bunch(patch=patch, flows=flows, angles=angles,
                                   tips=tips, text=text, texts=texts))

        # Allow a daisy-chained call structure (see docstring for the class).
        return self
 def get_axislabel_transform(self, axes):
     return Affine2D()  #axes.transData
Ejemplo n.º 39
0
def build_cloud(wordweights, 
        loose=False, seed=None, split_limit=2**-3, pad=1.10, visual_limit=2**-5,
        highest_weight=None ):
    """Convert a list of words and weights into a list of paths and weights.

    You should only use this function if you know what you're doing, or if
    you really don't want to cache the generated paths.  Otherwise just use
    the WordCloud class.

    Args:
        wordweights: An iterator of the form 
                [ (word, weight), (word, weight), ... ]
            such that the weights are in decreasing order.
        loose: If `true', words won't be broken up into rectangles after
            insertion.  This results in a looser cloud, generated faster.
        seed: A random seed to use
        split_limit: When words are approximated by rectangles, the rectangles
            will have dimensions less than split_limit.  Higher values result
            in a tighter cloud, at a cost of more CPU time.  The largest word
            has height 1.0.
        pad: Expand a word's bounding box by a factor of `pad' before
            inserting it.  This can actually result in a tighter cloud if you
            have many small words by leaving space between large words.
        visual_limit: Words with height smaller than visual_limit will be
            discarded.
        highest_weight: Experimental feature.  If you provide an upper bound
            on the weights that will be seen you don't have to provide words
            and weights sorted.  The resulting word cloud will be noticeably
            uglier.

    Generates:
        Tuples of the form (path, weight) such that:
            * No two paths intersect
            * Paths are fairly densely packed around the origin
            * All weights are normalized to fall in the interval [0, 1]
    """
    if seed is not None:
        random.seed(seed)

    font_properties = font_manager.FontProperties(
                family="sans", weight="bold", stretch="condensed")
    xheight = TextPath((0,0), "x", prop=font_properties).get_extents().expanded(pad,pad).height

    # These are magic numbers.  Most wordclouds will not exceed these bounds.
    # If they do, it will have to re-index all of the bounding boxes.
    index_bounds = (-16, -16, 16, 16)
    index = BboxQuadtree(index_bounds)

    if highest_weight is None:
        # Attempt to pull the first word and weight.  If we fail, the wordweights
        # list is empty and we should just quit.
        #
        # All this nonsense is to ensure it accepts an iterator of words
        # correctly.
        iterwords = iter(wordweights)
        try:
            first_word, first_weight = iterwords.next()
            iterwords = chain([(first_word, first_weight)], iterwords)
        except StopIteration:
            return

        # We'll scale all of the weights down by this much.
        weight_scale = 1.0/first_weight
    else:
        weight_scale = 1.0/highest_weight
        iterwords = iter(wordweights)

    bboxes = list()

    bounds = transforms.Bbox(((-0.5, -0.5), (-0.5, -0.5)))
    for tword, tweight in iterwords:
        weight = tweight*weight_scale
        if weight < visual_limit:
            # You're not going to be able to see the word anyway.  Quit
            # rendering words now.
            continue

        word_path = TextPath((0,0), tword, prop=font_properties)
        word_bbox = word_path.get_extents().expanded(pad, pad)
        # word_scale = weight/float(word_bbox.height)
        word_scale = weight/float(xheight)
        
        # When we build a TextPath at (0,0) it doesn't necessarily have
        # its corner at (0,0).  So we have to translate to the origin,
        # scale down, then translate to center it.  Feel free to simplify
        # this if you want.
        word_trans = Affine2D.identity().translate(
                                -word_bbox.xmin,
                                -word_bbox.ymin
                            ).scale(word_scale).translate(
                                -0.5*abs(word_bbox.width)*word_scale,
                                -0.5*abs(word_bbox.height)*word_scale )

        word_path = word_path.transformed(word_trans)

        word_bbox = word_path.get_extents().expanded(pad, pad)

        if weight > split_limit:
            # Big words we place carefully, trying to make the dimensions of
            # the cloud equal and center it around the origin.
            gaps = ( 
                    ("left", bounds.xmin), ("bottom", bounds.ymin), 
                    ("right", bounds.xmax), ("top", bounds.ymax) )
            direction = min(gaps, key=lambda g: abs(g[1]))[0]
        else:
            # Small words we place randomly.
            direction = random.choice( [ "left", "bottom", "right", "top" ] )

        # Randomly place the word along an edge...
        if direction in ( "top", "bottom" ):
            center = random_position(bounds.xmin, bounds.xmax)
        elif direction in ( "right", "left" ):
            center = random_position(bounds.ymin, bounds.ymax)

        # And push it toward an axis.
        if direction == "top":
            bbox = word_bbox.translated( center, index_bounds[3] )
            xpos, ypos = push_bbox_down( bbox, bboxes, index )
        elif direction == "right":
            bbox = word_bbox.translated( index_bounds[2], center )
            xpos, ypos = push_bbox_left( bbox, bboxes, index )
        elif direction == "bottom":
            bbox = word_bbox.translated( center, index_bounds[1] )
            xpos, ypos = push_bbox_up( bbox, bboxes, index )
        elif direction == "left":
            bbox = word_bbox.translated( index_bounds[0], center )
            xpos, ypos = push_bbox_right( bbox, bboxes, index )
    
        # Now alternate pushing the word toward different axes until either
        # it stops movign or we get sick of it.
        max_moves = 2
        moves = 0
        while moves < max_moves and (moves == 0 or prev_xpos != xpos or prev_ypos != ypos):
            moves += 1
            prev_xpos = xpos
            prev_ypos = ypos
            if direction in ["top", "bottom", "vertical"]:
                if xpos > 0:
                    bbox = word_bbox.translated( xpos, ypos )
                    xpos, ypos = push_bbox_left( bbox, bboxes, index )
                elif xpos < 0:
                    bbox = word_bbox.translated( xpos, ypos )
                    xpos, ypos = push_bbox_right( bbox, bboxes, index )
                direction = "horizontal"
            elif direction in ["left", "right", "horizontal"]:
                if ypos > 0:
                    bbox = word_bbox.translated( xpos, ypos )
                    xpos, ypos = push_bbox_down( bbox, bboxes, index )
                elif ypos < 0:
                    bbox = word_bbox.translated( xpos, ypos )
                    xpos, ypos = push_bbox_up( bbox, bboxes, index )
                direction = "vertical"

        wordtrans = Affine2D.identity().translate( xpos, ypos )

        transpath = word_path.transformed(wordtrans)
        bbox = transpath.get_extents()

        # Swallow the new word into the bounding box for the word cloud.
        bounds = matplotlib.transforms.Bbox.union( [ bounds, bbox ] )

        # We need to check if we've expanded past the bounds of our quad tree.
        # If so we'll need to expand the bounds and then re-index.
        new_bounds = index_bounds
        while not BoxifyWord.bbox_covers(
            # FIXME: Why am I not just doing this with a couple of logarithms?
                    matplotlib.transforms.Bbox(((new_bounds[0], new_bounds[1]),
                                                (new_bounds[2], new_bounds[3]))),
                    bounds ):
            new_bounds = tuple( map( lambda x: 2*x, index_bounds ) )

        if new_bounds != index_bounds:
            # We need to re-index.
            index_bounds = new_bounds
            index = BboxQuadtree(index_bounds)
            for i, b in enumerate(bboxes):
                index.add_bbox(i, b)

        # Approximate the new word by rectangles (unless it's too small) and
        # insert them into the index.
        if not loose and max(abs(bbox.width), abs(bbox.height)) > split_limit:
            for littlebox in BoxifyWord.splitword( 
                    bbox, transpath, limit=split_limit ):
                bboxes.append( littlebox )
                index.add_bbox( len(bboxes)-1, littlebox )
        else:
            bboxes.append( bbox )
            index.add_bbox( len(bboxes)-1, bbox )

        yield (transpath, weight)