Example #1
0
    def __init__(self, transform, sizex=0, sizey=0, labelx=None, labely=None, loc=4,
                 pad=0.1, borderpad=0.1, sep=2, prop=None, frameon=False, **kwargs):
        """
        Draw a horizontal and/or vertical  bar with the size in data coordinate
        of the give axes. A label will be drawn underneath (center-aligned).

        - transform : the coordinate frame (typically axes.transData)
        - sizex,sizey : width of x,y bar, in data units. 0 to omit
        - labelx,labely : labels for x,y bars; None to omit
        - loc : position in containing axes
        - pad, borderpad : padding, in fraction of the legend font size (or prop)
        - sep : separation between labels and bars in points.
        - **kwargs : additional arguments passed to base class constructor
        """
        from matplotlib.patches import Rectangle
        from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea
        bars = AuxTransformBox(transform)
        if sizex:
            bars.add_artist(Rectangle((0,0), sizex, 0, fc="none"))
        if sizey:
            bars.add_artist(Rectangle((0,0), 0, sizey, fc="none"))

        if sizex and labelx:
            bars = VPacker(children=[bars, TextArea(labelx, minimumdescent=False)],
                           align="center", pad=0, sep=sep)
        if sizey and labely:
            bars = HPacker(children=[TextArea(labely), bars],
                            align="center", pad=0, sep=sep)

        res = AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
                                   child=bars, prop=prop, frameon=frameon, **kwargs)
        return res
Example #2
0
def multicolor_xlabel(ax,
                      list_of_strings,
                      list_of_colors,
                      h=0.0,
                      anchorpad=0,
                      **kw):
    """this function creates axes labels with multiple colors
    ax specifies the axes object where the labels should be drawn
    list_of_strings is a list of all of the text items
    list_if_colors is a corresponding list of colors for the strings
    axis='x', 'y', or 'both' and specifies which label(s) should be drawn"""
    from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, HPacker

    boxes = [
        TextArea(text,
                 textprops=dict(color=color, ha='left', va='bottom', **kw))
        for text, color in zip(list_of_strings, list_of_colors)
    ]
    xbox = HPacker(children=boxes, align="bottom", pad=0, sep=5)
    anchored_xbox = AnchoredOffsetbox(loc=3,
                                      child=xbox,
                                      pad=anchorpad,
                                      frameon=False,
                                      bbox_to_anchor=(0, h),
                                      bbox_transform=ax.transAxes,
                                      borderpad=0.)
    ax.add_artist(anchored_xbox)
Example #3
0
def logo_box(x, y):

    logo_offset_image = OffsetImage(read_png(
        get_sample_data(logo_location, asfileobj=False)),
                                    zoom=0.25,
                                    resample=1,
                                    dpi_cor=1)
    text_box = TextArea(logo_text,
                        textprops=dict(color='#444444',
                                       fontsize=50,
                                       weight='bold'))

    logo_and_text_box = HPacker(children=[logo_offset_image, text_box],
                                align="center",
                                pad=0,
                                sep=25)

    anchored_box = AnchoredOffsetbox(loc=2,
                                     child=logo_and_text_box,
                                     pad=0.8,
                                     frameon=False,
                                     borderpad=0.,
                                     bbox_to_anchor=[x, y],
                                     bbox_transform=plt.gcf().transFigure)

    return anchored_box
Example #4
0
def colortext(x, y, texts, colors, **kwargs):
    pos = {"right": 1, "center": 0.5, "left": 0, "top": 0, "bottom": 1}

    ax = kwargs.get("ax")
    verticalalignment = pos[kwargs.get("verticalalignment", "center")]
    horizontalalignment = pos[kwargs.get("horizontalalignment", "center")]
    annotation_clip = kwargs.get("clip_on", False)
    fontproperties = kwargs.get("fontproperties", None)
    textprops = {"fontproperties": fontproperties}
    transform = kwargs.get("transform", None)

    areas = []
    for t, c in zip(texts, colors):
        textprops["color"] = c
        text = TextArea(t, textprops=textprops)
        areas.append(text)

    txt = HPacker(children=areas, align="baseline", pad=0, sep=0)

    bbox = AnnotationBbox(
        txt,
        xy=(x, y),
        xycoords='data',
        annotation_clip=annotation_clip,
        frameon=False,
        boxcoords=("axes fraction"),
        box_alignment=(horizontalalignment,
                       verticalalignment),  # alignment center, center
        #bboxprops={"bbox_transmuter":transform},
    )

    ax.add_artist(bbox)
Example #5
0
def generate_title(d_min, d_max, ax):
    '''This generates the title box artist to be added to the top of the map.
    If the title needs to be changed, this is where to do it.

    The dates d_min and d_max should be years.

    If the title location needs to be adjusted, this is where that happens
    (bbox_to_anchor).
    '''
    title = TextArea("Cruises for the U.S. Global Ocean Carbon and Repeat "
            "Hydrography Program, {0}-{1}".format(d_min, d_max), textprops=dict(size=9))
    subtitle_size = 8
    subt1 = TextArea("(", textprops=dict(size=subtitle_size))
    subt2 = TextArea("red italic", textprops=dict(color="r",style="italic",size=subtitle_size))
    subt3 = TextArea("indicates pending cruise;", textprops=dict(size=subtitle_size))
    subt4 = TextArea("grey", textprops=dict(color="grey",size=subtitle_size))
    subt5 = TextArea("indicates completed cruises; black indicates funded cruises)",
            textprops=dict(size=subtitle_size))
    
    subt = HPacker(children=[subt1,subt2,subt3,subt4,subt5], align="center", pad=0,
            sep=2)
    title = VPacker(children=[title,subt], align="center", pad=0, sep=8)
    t = AnchoredOffsetbox(loc=3, child=title, pad=0, bbox_to_anchor=(-0.02, 1.02),
            bbox_transform=ax.transAxes, borderpad=0, frameon=False)
    return t
Example #6
0
    def __init__(
        self,
        transform,
        sizex=0,
        sizey=0,
        labelx=None,
        labely=None,
        loc=4,
        pad=0.1,
        borderpad=0.1,
        sep=2,
        prop=None,
        barcolor="black",
        barwidth=None,
        label_kwa={},
        **kwargs,
    ):
        """
        Draw a horizontal and/or vertical  bar with the size in data coordinate
        of the give axes. A label will be drawn underneath (center-aligned).

        Parameters
        ----------
        - transform : the coordinate frame (typically axes.transData)
        - sizex,sizey : width of x,y bar, in data units. 0 to omit
        - labelx,labely : labels for x,y bars; None to omit
        - loc : position in containing axes
        - pad, borderpad : padding, in fraction of the legend font size
          (or prop)
        - sep : separation between labels and bars in points.
        - **kwargs : additional arguments passed to base class constructor
        """
        from matplotlib.offsetbox import AuxTransformBox, HPacker, TextArea, VPacker
        from matplotlib.patches import Rectangle

        bars = AuxTransformBox(transform)
        rect_kwa = {"ec": barcolor, "lw": barwidth, "fc": "none"}
        if sizex:
            bars.add_artist(Rectangle((0, 0), sizex, 0, **rect_kwa))
        if sizey:
            bars.add_artist(Rectangle((0, 0), 0, sizey, **rect_kwa))

        vpacker_kwa = {"align": "center", "pad": 0, "sep": sep}
        if sizex and labelx:
            self.xlabel = TextArea(labelx, minimumdescent=False, **label_kwa)
            bars = VPacker(children=[bars, self.xlabel], **vpacker_kwa)
        if sizey and labely:
            self.ylabel = TextArea(labely, **label_kwa)
            bars = HPacker(children=[self.ylabel, bars], **vpacker_kwa)

        AnchoredOffsetbox.__init__(
            self,
            loc,
            pad=pad,
            borderpad=borderpad,
            child=bars,
            prop=prop,
            frameon=False,
            **kwargs,
        )
Example #7
0
def make_gui(img, dataset_name, **kwargs):
    global alphabet
    fig = plt.figure(figsize=kwargs["figsize"])
    from matplotlib import rcParams
    rcParams['axes.linewidth'] = 2
    rcParams['axes.edgecolor'] = 'k'

    plt.imshow(img)

    label_category = cv_label_category if dataset_name == "camvid" else voc_label_category
    alphabet = alphabet_cv if dataset_name == "camvid" else alphabet_voc

    vpacker_children = [TextArea("{} - {}".format(alphabet[l], cat), textprops={"weight": 'bold', "size": 10})
                        for l, cat in sorted(label_category.items(), key=lambda x: x[1])]
    box = VPacker(children=vpacker_children, align="left", pad=5, sep=5)

    # display the texts on the right side of image
    anchored_box = AnchoredOffsetbox(loc="center left",
                                     child=box,
                                     pad=0.,
                                     frameon=True,
                                     bbox_to_anchor=(1.04, 0.5),
                                     bbox_transform=plt.gca().transAxes,
                                     borderpad=0.)
    anchored_box.patch.set_linewidth(2)
    anchored_box.patch.set_facecolor('gray')
    anchored_box.patch.set_alpha(0.2)

    anchored_box.patch.set_boxstyle("round,pad=0.5, rounding_size=0.2")
    plt.gca().add_artist(anchored_box)

    # create texts for "Enter a label for the current marker"
    box1 = TextArea("Enter a label for the current marker",
                    textprops={"weight": 'bold', "size": 12})
    box2 = DrawingArea(5, 10, 0, 0)
    box2.add_artist(mpatches.Circle((5, 5), radius=5, fc=np.array((1, 0, 0)), edgecolor="k", lw=1.5))
    box = HPacker(children=[box1, box2], align="center", pad=5, sep=5)

    # anchored_box creates the text box outside of the plot
    anchored_box = AnchoredOffsetbox(loc="lower center",
                                     child=box,
                                     pad=0.,
                                     frameon=False,
                                     bbox_to_anchor=(0.5, -0.1),  # ( 0.5, -0.1)
                                     bbox_transform=plt.gca().transAxes,
                                     borderpad=0.)
    plt.gca().add_artist(anchored_box)
    plt.xticks([])
    plt.yticks([])
    plt.tight_layout(pad=2)

    buf = io.BytesIO()
    fig.savefig(buf, format="jpg", dpi=80)
    buf.seek(0)
    img_arr = np.frombuffer(buf.getvalue(), dtype=np.uint8)
    buf.close()
    im = cv2.imdecode(img_arr, 1)
    plt.close()
    return im
Example #8
0
def export_panel(plitem, ax):
    """
    export_panel recreates the contents of one pyqtgraph PlotItem into a specified
    matplotlib axis item.
    Handles PlotItem types of PlotCurveItem, PlotDataItem, BarGraphItem, and ScatterPlotItem
    :param plitem: The PlotItem holding a single plot
    :param ax: the matplotlib axis to put the result into
    :return: Nothing
    """
    # get labels from the pyqtgraph PlotItems
    xlabel = plitem.axes['bottom']['item'].label.toPlainText()
    ylabel = plitem.axes['left']['item'].label.toPlainText()
    title = remove_html_markup(plitem.titleLabel.text)
    label = remove_html_markup(plitem.plotLabel.text)
    fontsize = 12
    fn = pg.functions
    ax.clear()
    cleanAxes(ax)  # make a "nice" plot
    ax.set_title(title)  # add the plot title here
    for item in plitem.items:  # was .curves, but let's handle all items
        # dispatch do different routines depending on what we need to plot
        if isinstance(item, pg.graphicsItems.PlotCurveItem.PlotCurveItem):
            export_curve(fn, ax, item)
        elif isinstance(item, pg.graphicsItems.PlotDataItem.PlotDataItem):
            export_curve(fn, ax, item)
        elif isinstance(item, pg.graphicsItems.BarGraphItem.BarGraphItem):
            export_bargraph(fn, ax, item)
        elif isinstance(item,
                        pg.graphicsItems.ScatterPlotItem.ScatterPlotItem):
            export_scatterplot(fn, ax, item)
        else:
            print 'unknown item encountered : ', item
            continue
    xr, yr = plitem.viewRange()
    # now clean up the matplotlib/pylab plot and annotations
    ax.set_xbound(*xr)
    ax.set_ybound(*yr)
    at = TextArea(label,
                  textprops=dict(color="k",
                                 verticalalignment='bottom',
                                 weight='bold',
                                 horizontalalignment='right',
                                 fontsize=fontsize,
                                 family='sans-serif'))
    box = HPacker(children=[at], align="left", pad=0, sep=2)
    ab = AnchoredOffsetbox(loc=3,
                           child=box,
                           pad=0.,
                           frameon=False,
                           bbox_to_anchor=(-0.08, 1.1),
                           bbox_transform=ax.transAxes,
                           borderpad=0.)
    ax.add_artist(ab)
    ax.set_xlabel(xlabel)  # place the axis labels.
    ax.set_ylabel(ylabel)
Example #9
0
def make_line_key(label, color):
    label = str(label)
    idx = len(label)
    pad = 20 - idx
    lab = label[:max(idx, 20)]
    pad = " " * pad
    label = TextArea(": %s" % lab, textprops=dict(color="k"))
    viz = DrawingArea(20, 20, 0, 0)
    viz.add_artist(Rectangle((0, 5), width=16, height=5, fc=color))
    return HPacker(children=[viz, label],
                   height=25,
                   align="center",
                   pad=5,
                   sep=0)
Example #10
0
def make_marker_key(label, marker):
    idx = len(label)
    pad = 20 - idx
    lab = label[:max(idx, 20)]
    pad = " " * pad
    label = TextArea(": %s" % lab, textprops=dict(color="k"))
    viz = DrawingArea(15, 20, 0, 0)
    fontsize = 10
    key = mlines.Line2D([0.5 * fontsize], [0.75 * fontsize],
                        marker=marker,
                        markersize=(0.5 * fontsize),
                        c="k")
    viz.add_artist(key)
    return HPacker(children=[viz, label], align="center", pad=5, sep=0)
Example #11
0
def make_linestyle_key(label, style):
    idx = len(label)
    pad = 20 - idx
    lab = label[:max(idx, 20)]
    pad = " " * pad
    label = TextArea(": %s" % lab, textprops=dict(color="k"))
    viz = DrawingArea(30, 20, 0, 0)
    fontsize = 10
    x = np.arange(0.5, 2.25, 0.25) * fontsize
    y = np.repeat(0.75, 7) * fontsize

    key = mlines.Line2D(x, y, linestyle=style, c="k")
    viz.add_artist(key)
    return HPacker(children=[viz, label], align="center", pad=5, sep=0)
Example #12
0
        def rainbow_text(x, y, ls, lc, **kw):
            """
        Take a list of strings ``ls`` and colors ``lc`` and place them next to each
        other, with text ls[i] being shown in color lc[i].

        This example shows how to do both vertical and horizontal text, and will
        pass all keyword arguments to plt.text, so you can set the font size,
        family, etc.
        """
            from matplotlib.offsetbox import HPacker, TextArea, AnnotationBbox

            ax = pylab.gca()
            texts = [
                TextArea(s, textprops=dict(color=c, family='serif'))
                for (s, c) in zip(ls, lc)
            ]
            txt = HPacker(children=texts, align="baseline", pad=0, sep=0)

            def txt_offset(*kl):
                return ax.transData.transform_point((x, y))

            txt.set_offset(txt_offset)

            ax.add_artist(txt)
Example #13
0
def offset_text(ax, txt, loc, bbox_to_anchor=(-.03, .65)):
    box1 = TextArea(txt, textprops=dict(color="k", size='10'))

    box = HPacker(children=[box1], align="center", pad=2, sep=5)

    anchored_box = AnchoredOffsetbox(
        loc=loc,
        child=box,
        pad=0.,
        frameon=False,
        prop=dict(size=5),
        bbox_to_anchor=bbox_to_anchor,
        bbox_transform=ax.transAxes,
        borderpad=0.1,
    )
    ax.add_artist(anchored_box)
Example #14
0
def make_size_key(label, size):
    if not isinstance(label, six.string_types):
        label = round(label, 2)
        label = str(label)
    idx = len(label)
    pad = 20 - idx
    lab = label[:max(idx, 20)]
    pad = " " * pad
    label = TextArea("  %s" % lab, textprops=dict(color="k"))
    viz = DrawingArea(15, 20, 0, 0)
    fontsize = 10
    key = mlines.Line2D([0.5 * fontsize], [0.75 * fontsize],
                        marker="o",
                        markersize=size / 20.,
                        c="k")
    viz.add_artist(key)
    return HPacker(children=[viz, label], align="center", pad=5, sep=0)
Example #15
0
def multicolor_label(ax, list_of_strings, list_of_colors, axis="x", anchorpad=0, **kw):
    """this function creates axes labels with multiple colors
    ax specifies the axes object where the labels should be drawn
    list_of_strings is a list of all of the text items
    list_if_colors is a corresponding list of colors for the strings
    axis='x', 'y', or 'both' and specifies which label(s) should be drawn"""
    from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, HPacker, VPacker

    # x-axis label
    if axis == "x" or axis == "both":
        boxes = [
            TextArea(text, textprops=dict(color=color, ha="left", va="bottom", **kw))
            for text, color in zip(list_of_strings, list_of_colors)
        ]
        xbox = HPacker(children=boxes, align="bottom", pad=0, sep=20)
        anchored_xbox = AnchoredOffsetbox(
            loc="center",
            child=xbox,
            pad=anchorpad,
            frameon=False,
            bbox_to_anchor=(0.5, -0.12),
            bbox_transform=ax.transAxes,
            borderpad=0.0,
        )
        ax.add_artist(anchored_xbox)

    # y-axis label
    if axis == "y" or axis == "both":
        boxes = [
            TextArea(
                text,
                textprops=dict(color=color, ha="left", va="bottom", rotation=90, **kw),
            )
            for text, color in zip(list_of_strings[::-1], list_of_colors)
        ]
        ybox = VPacker(children=boxes, align="center", pad=0, sep=20)
        anchored_ybox = AnchoredOffsetbox(
            loc=3,
            child=ybox,
            pad=anchorpad,
            frameon=False,
            bbox_to_anchor=(-0.12, 0.5),
            bbox_transform=ax.transAxes,
            borderpad=0.0,
        )
        ax.add_artist(anchored_ybox)
Example #16
0
def multicolor_xlabel(ax, list_of_strings, anchorpad=0, **kw):
    # code from: https://stackoverflow.com/a/33162465
    from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, HPacker, VPacker
    boxes = [
        TextArea(text,
                 textprops=dict(color='C{}'.format(i),
                                ha='left',
                                va='bottom',
                                **kw))
        for i, text in enumerate(list_of_strings)
    ]
    xbox = HPacker(children=boxes, align='center', pad=0, sep=5)
    anchored_xbox = AnchoredOffsetbox(loc='lower center',
                                      child=xbox,
                                      pad=anchorpad,
                                      frameon=False,
                                      bbox_transform=ax.transAxes,
                                      borderpad=0.0)
    ax.add_artist(anchored_xbox)
Example #17
0
def multicolor_label(ax,
                     list_of_strings,
                     list_of_colors,
                     axis='x',
                     anchorpad=0,
                     bbox_to_anchor=(0, 0),
                     **kw):
    from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, HPacker, VPacker

    # x-axis label
    if axis == 'x' or axis == 'both':
        boxes = [
            TextArea(text,
                     textprops=dict(color=color, ha='left', va='bottom', **kw))
            for text, color in zip(list_of_strings, list_of_colors)
        ]
        xbox = HPacker(children=boxes, align="center", pad=0, sep=5)
        anchored_xbox = AnchoredOffsetbox(loc='center left',
                                          child=xbox,
                                          pad=anchorpad,
                                          frameon=False,
                                          bbox_to_anchor=bbox_to_anchor,
                                          bbox_transform=ax.transAxes,
                                          borderpad=0.)
        ax.add_artist(anchored_xbox)

    # y-axis label
    if axis == 'y' or axis == 'both':
        boxes = [
            TextArea(text,
                     textprops=dict(color=color, ha='left', va='center', **kw))
            for text, color in zip(list_of_strings[::-1], list_of_colors)
        ]
        ybox = VPacker(children=boxes, align='center', pad=0, sep=5)
        anchored_ybox = AnchoredOffsetbox(loc='center left',
                                          child=ybox,
                                          pad=anchorpad,
                                          frameon=False,
                                          bbox_to_anchor=bbox_to_anchor,
                                          bbox_transform=ax.transAxes,
                                          borderpad=0.)
        ax.add_artist(anchored_ybox)
Example #18
0
def draw_legend_group(legends, column_name, ith_group):
    labels = get_legend_labels(legends)
    colors, has_color = get_colors(legends)
    alphas, has_alpha = get_alphas(legends)
    legend_title = make_title(column_name)
    legend_labels = [make_label(l) for l in labels]
    none_dict = defaultdict(lambda: None)
    legend_cols = []

    # Draw shapes if any (discs for sizes)
    if "shape" in legends or "size" in legends:
        shapes = legends["shape"] if "shape" in legends else none_dict
        sizes = legends["size"] if "size" in legends else none_dict
        line = lambda l: make_shape(colors[l], shapes[l], sizes[l], alphas[l])
        legend_shapes = [line(label) for label in labels]
        legend_cols.append(legend_shapes)

    # Draw lines if any
    if "linetype" in legends:
        linetypes = legends["linetype"]
        legend_lines = [
            make_line(colors[l], linetypes[l], alphas[l]) for l in labels
        ]
        legend_cols.append(legend_lines)

    # If we don't have lines, legends or sizes, indicate color with a rectangle
    already_drawn = set(["linetype", "shape", "size"])
    if (len(already_drawn.intersection(legends)) == 0
            and (has_color or has_alpha)):
        legend_rects = [make_rect(colors[l], alphas[l]) for l in labels]
        legend_cols.append(legend_rects)

    # Concatenate columns and compile rows
    legend_cols.append(legend_labels)
    row = lambda l: HPacker(
        children=l, height=25, align='center', pad=5, sep=0)
    legend_rows = [row(legend_items) for legend_items in zip(*legend_cols)]

    # Vertically align items and anchor in plot
    rows = [legend_title] + legend_rows
    box = VPacker(children=rows, align="left", pad=0, sep=-10)
    return box, len(rows)
Example #19
0
def printNode(node):


    fig, ax = plt.subplots()

    infoBox = TextArea("        -- Node -- " + \
    					"\n · Placed: " + str(node.buildingType.name) + \
    					"\n · At cell " + str(node.buildingPosition) + \
					    "\n · Node weight: " + str(node.nodeWeight) + \
					    "\n · Expanded "+ str(node.getSearchLevel()) + " levels" + \
					    "\n · Contains " + str((node.getRegressedMatrix()).findUnbiltHolesRounded()) + " holes")
    
    box = HPacker(children=[infoBox],
              align="center",
              pad=5, sep=5)

    anchored_box = AnchoredOffsetbox(loc=3,
                                 child=box, pad=0.,
                                 frameon=True,
                                 bbox_to_anchor=(1.02, 0.8),
                                 bbox_transform=ax.transAxes,
                                 borderpad=0.,
                                 )

    matrix = node.getRegressedMatrix().matrixMap
    ax.matshow(matrix)

    ax.add_artist(anchored_box)
    fig.subplots_adjust(right=0.9)
    fig.subplots_adjust(top=0.97)
    fig.subplots_adjust(bottom=0.02)

    for x in range(matrix.shape[0]):
        for y in range(matrix.shape[1]):
            c = matrix[x,y]
            #if not math.isinf(c) and  c != float("inf") and c != float("-inf"):
            #    c = int(c)
            ax.text(y,x, str(int(c)), va='center', ha='center')

    plt.show()
Example #20
0
 def __init__(self, transform, sizex=0, sizey=0, labelx=None, labely=None, loc=4,
              pad=0.1, borderpad=0.1, sep=2, prop=None, **kwargs):
     
     from matplotlib.patches import Rectangle
     from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea
     
     bars = AuxTransformBox(transform)
     
     if sizex:
         bars.add_artist(Rectangle((0,0), sizex, 0, fc="none"))
     if sizey:
         bars.add_artist(Rectangle((0,0), 0, sizey, fc="none"))
     
     if sizex and labelx:
         bars = VPacker(children=[bars, TextArea(labelx, minimumdescent=False)],
                        align="center", pad=0, sep=sep)
     if sizey and labely:
         bars = HPacker(children=[TextArea(labely), bars],
                         align="center", pad=0, sep=sep)
     
     AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
                                child=bars, prop=prop, frameon=False, **kwargs)
Example #21
0
    def __init__(self, transform, sizex=0, sizey=0, labelx=None, labely=None, loc=4,
                 pad=0.1, borderpad=0.1, sep=2, prop=None, label_fontsize=label_fontsize, color='k', **kwargs):
        """
        Draw a horizontal and/or vertical  bar with the size in data coordinate
        of the give axes. A label will be drawn underneath (center-aligned).

        - transform : the coordinate frame (typically axes.transData)
        - sizex,sizey : width of x,y bar, in data units. 0 to omit
        - labelx,labely : labels for x,y bars; None to omit
        - loc : position in containing axes
        - pad, borderpad : padding, in fraction of the legend font size (or prop)
        - sep : separation between labels and bars in points.
        - **kwargs : additional arguments passed to base class constructor
        """
        from matplotlib.patches import Rectangle
        from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea
        bars = AuxTransformBox(transform)
        if sizex:
            bars.add_artist(Rectangle((0,0), sizex, 0, fc="none", linewidth=axes_linewidth, color=color))
        if sizey:
            bars.add_artist(Rectangle((0,0), 0, sizey, fc="none", linewidth=axes_linewidth, color=color))

        if sizex and labelx:
            textareax = TextArea(labelx,minimumdescent=False,textprops=dict(size=label_fontsize,color=color))
            bars = VPacker(children=[bars, textareax], align="center", pad=0, sep=sep)
        if sizey and labely:
            ## VPack a padstr below the rotated labely, else label y goes below the scale bar
            ## Just adding spaces before labely doesn't work!
            padstr = '\n '*len(labely)
            textareafiller = TextArea(padstr,textprops=dict(size=label_fontsize/3.0))
            textareay = TextArea(labely,textprops=dict(size=label_fontsize,rotation='vertical',color=color))
            ## filler / pad string VPack-ed below labely
            textareayoffset = VPacker(children=[textareay, textareafiller], align="center", pad=0, sep=sep)
            ## now HPack this padded labely to the bars
            bars = HPacker(children=[textareayoffset, bars], align="top", pad=0, sep=sep)

        AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
                                   child=bars, prop=prop, frameon=False, **kwargs)
Example #22
0
    def _generate_lines(self):
        """
        Docstring
        """
        opn, sep, clo = self.flag

        expr = f"([\{opn}].*?[\{clo}])"
        parts = re.split(expr, self.string)

        expr = f"[\{opn}](.*?)[\{sep}](.*?)[\{clo}]"
        lines = [[]]
        n = 0
        for part in parts:
            opts = self.base.copy()

            p = re.match(expr, part)
            if p:
                part, fmt = p.groups()
                fmt = int(fmt)
                opts.update(self.highlight[fmt])

            rows = part.split('\n')
            for i, row in enumerate(rows):
                if i != 0:
                    lines.append([])
                    n += 1

                txt = TextArea(row, textprops=opts)
                lines[n].append(txt)

        boxes = []
        for line in lines:
            box = HPacker(children=line, align="baseline", pad=0, sep=0)
            boxes.append(box)

        self.boxes = boxes
        return boxes
Example #23
0
def sbs_to_patch(sbs,
                 transform,
                 unit,
                 padding=2,
                 bbox_transform='axes fraction',
                 bbox_to_anchor=(0.2, 0.3)):

    # First create a single Patch for all Sbs
    of1 = HPacker(width=2,
                  height=1,
                  pad=1,
                  sep=0,
                  align="center",
                  mode="expand",
                  children=sbs)

    t = AnchoredOffsetbox("upper left",
                          pad=0.4,
                          frameon=False,
                          bbox_transform=bbox_transform,
                          bbox_to_anchor=bbox_to_anchor,
                          child=of1)

    return t
Example #24
0
def offset_text(ax,
                txt,
                loc,
                bbox_to_anchor=(-.03, .65),
                text_props={
                    "color": "k",
                    "size": "13"
                }):
    """This function is a copy of the one in draw.py"""
    box1 = TextArea(txt, textprops=text_props)

    box = HPacker(children=[box1], align="center", pad=2, sep=5)

    anchored_box = AnchoredOffsetbox(
        loc=loc,
        child=box,
        pad=0.,
        frameon=False,
        prop=dict(size=5),
        bbox_to_anchor=bbox_to_anchor,
        bbox_transform=ax.transAxes,
        borderpad=0.1,
    )
    ax.add_artist(anchored_box)
    def plot_rectangle(self, figure, axis):
        '''
        plots the legend rectangle above the left corner of the figure
        :param figure: figure on which to add the label
        :param axis: axis on which to add the label
        :return: -
        '''

        box1 = TextArea(" True: \n False: \n NaN: ",
                        textprops=dict(color="k", size=10))

        # box2 = DrawingArea(20, 27.5, 0, 0)
        # el1 = Rectangle((5, 15), width=10, height=10, angle=0, fc="g")
        # el2 = Rectangle((5, 2.5), width=10, height=10, angle=0, fc="r")
        box2 = DrawingArea(20, 45, 0, 0)
        el1 = Rectangle((5, 30), width=10, height=10, angle=0, fc="g")
        el2 = Rectangle((5, 18.5), width=10, height=10, angle=0, fc="r")
        el3 = Rectangle((5, 7), width=10, height=10, angle=0, fc='#d3d3d3')
        box2.add_artist(el1)
        box2.add_artist(el2)
        box2.add_artist(el3)

        box = HPacker(children=[box1, box2], align="center", pad=0, sep=5)

        anchored_box = AnchoredOffsetbox(
            loc=3,
            child=box,
            pad=0.,
            frameon=True,
            bbox_to_anchor=(0., 1.02),
            bbox_transform=axis.transAxes,
            borderpad=0.,
        )

        axis.add_artist(anchored_box)
        figure.subplots_adjust(top=0.8)
Example #26
0
    def _init_legend_box(self, handles, labels, markerfirst=True):
        """
        Initialize the legend_box. The legend_box is an instance of
        the OffsetBox, which is packed with legend handles and
        texts. Once packed, their location is calculated during the
        drawing time.
        """

        fontsize = self._fontsize

        # legend_box is a HPacker, horizontally packed with
        # columns. Each column is a VPacker, vertically packed with
        # legend items. Each legend item is HPacker packed with
        # legend handleBox and labelBox. handleBox is an instance of
        # offsetbox.DrawingArea which contains legend handle. labelBox
        # is an instance of offsetbox.TextArea which contains legend
        # text.

        text_list = []  # the list of text instances
        handle_list = []  # the list of text instances

        label_prop = dict(
            verticalalignment='baseline',
            horizontalalignment='left',
            fontproperties=self.prop,
        )

        labelboxes = []
        handleboxes = []

        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        descent = 0.35 * self._approx_text_height() * (self.handleheight - 0.7)
        # 0.35 and 0.7 are just heuristic numbers and may need to be improved.
        height = self._approx_text_height() * self.handleheight - descent
        # each handle needs to be drawn inside a box of (x, y, w, h) =
        # (0, -descent, width, height).  And their coordinates should
        # be given in the display coordinates.

        # The transformation of each handle will be automatically set
        # to self.get_trasnform(). If the artist does not use its
        # default transform (e.g., Collections), you need to
        # manually set their transform to the self.get_transform().
        legend_handler_map = self.get_legend_handler_map()

        for orig_handle, lab in zip(handles, labels):
            handler = self.get_legend_handler(legend_handler_map, orig_handle)
            if handler is None:
                warnings.warn(
                    "Legend does not support {!r} instances.\nA proxy artist "
                    "may be used instead.\nSee: "
                    "http://matplotlib.org/users/legend_guide.html"
                    "#using-proxy-artist".format(orig_handle))
                # We don't have a handle for this artist, so we just defer
                # to None.
                handle_list.append(None)
            else:
                textbox = TextArea(lab,
                                   textprops=label_prop,
                                   multilinebaseline=True,
                                   minimumdescent=True)
                text_list.append(textbox._text)

                labelboxes.append(textbox)

                handlebox = DrawingArea(width=self.handlelength * fontsize,
                                        height=height,
                                        xdescent=0.,
                                        ydescent=descent)
                handleboxes.append(handlebox)

                # Create the artist for the legend which represents the
                # original artist/handle.
                handle_list.append(
                    handler.legend_artist(self, orig_handle, fontsize,
                                          handlebox))

        if len(handleboxes) > 0:

            # We calculate number of rows in each column. The first
            # (num_largecol) columns will have (nrows+1) rows, and remaining
            # (num_smallcol) columns will have (nrows) rows.
            ncol = min(self._ncol, len(handleboxes))
            nrows, num_largecol = divmod(len(handleboxes), ncol)
            num_smallcol = ncol - num_largecol

            # starting index of each column and number of rows in it.
            largecol = safezip(
                list(xrange(0, num_largecol * (nrows + 1), (nrows + 1))),
                [nrows + 1] * num_largecol)
            smallcol = safezip(
                list(
                    xrange(num_largecol * (nrows + 1), len(handleboxes),
                           nrows)), [nrows] * num_smallcol)
        else:
            largecol, smallcol = [], []

        handle_label = safezip(handleboxes, labelboxes)
        columnbox = []
        for i0, di in largecol + smallcol:
            # pack handleBox and labelBox into itemBox
            itemBoxes = [
                HPacker(pad=0,
                        sep=self.handletextpad * fontsize,
                        children=[h, t] if markerfirst else [t, h],
                        align="baseline") for h, t in handle_label[i0:i0 + di]
            ]
            # minimumdescent=False for the text of the last row of the column
            if markerfirst:
                itemBoxes[-1].get_children()[1].set_minimumdescent(False)
            else:
                itemBoxes[-1].get_children()[0].set_minimumdescent(False)

            # pack columnBox
            if markerfirst:
                alignment = "baseline"
            else:
                alignment = "right"
            columnbox.append(
                VPacker(pad=0,
                        sep=self.labelspacing * fontsize,
                        align=alignment,
                        children=itemBoxes))

        if self._mode == "expand":
            mode = "expand"
        else:
            mode = "fixed"

        sep = self.columnspacing * fontsize
        self._legend_handle_box = HPacker(pad=0,
                                          sep=sep,
                                          align="baseline",
                                          mode=mode,
                                          children=columnbox)
        self._legend_title_box = TextArea("")
        self._legend_box = VPacker(
            pad=self.borderpad * fontsize,
            sep=self.labelspacing * fontsize,
            align="center",
            children=[self._legend_title_box, self._legend_handle_box])
        self._legend_box.set_figure(self.figure)
        self.texts = text_list
        self.legendHandles = handle_list
Example #27
0
    def do_plot(self, wallet, history):
        balance_Val = []
        fee_val = []
        value_val = []
        datenums = []
        unknown_trans = 0
        pending_trans = 0
        counter_trans = 0
        balance = 0
        for item in history:
            tx_hash, confirmations, value, timestamp = item
            balance += value
            if confirmations:
                if timestamp is not None:
                    try:
                        datenums.append(
                            md.date2num(
                                datetime.datetime.fromtimestamp(timestamp)))
                        balance_string = format_satoshis(balance, False)
                        balance_Val.append(
                            float((format_satoshis(balance, False))) * 1000.0)
                    except [RuntimeError, TypeError, NameError] as reason:
                        unknown_trans += 1
                        pass
                else:
                    unknown_trans += 1
            else:
                pending_trans += 1

            value_string = format_satoshis(value, True)
            value_val.append(float(value_string) * 1000.0)

            if tx_hash:
                label, is_default_label = wallet.get_label(tx_hash)
                label = label.encode('utf-8')
            else:
                label = ""

        f, axarr = plt.subplots(2, sharex=True)

        plt.subplots_adjust(bottom=0.2)
        plt.xticks(rotation=25)
        ax = plt.gca()
        x = 19
        test11 = "Unknown transactions =  " + str(
            unknown_trans) + " Pending transactions =  " + str(
                pending_trans) + " ."
        box1 = TextArea(" Test : Number of pending transactions",
                        textprops=dict(color="k"))
        box1.set_text(test11)

        box = HPacker(children=[box1], align="center", pad=0.1, sep=15)

        anchored_box = AnchoredOffsetbox(
            loc=3,
            child=box,
            pad=0.5,
            frameon=True,
            bbox_to_anchor=(0.5, 1.02),
            bbox_transform=ax.transAxes,
            borderpad=0.5,
        )

        ax.add_artist(anchored_box)

        plt.ylabel('mZCL')
        plt.xlabel('Dates')
        xfmt = md.DateFormatter('%Y-%m-%d')
        ax.xaxis.set_major_formatter(xfmt)

        axarr[0].plot(datenums,
                      balance_Val,
                      marker='o',
                      linestyle='-',
                      color='blue',
                      label='Balance')
        axarr[0].legend(loc='upper left')
        axarr[0].set_title('History Transactions')

        xfmt = md.DateFormatter('%Y-%m-%d')
        ax.xaxis.set_major_formatter(xfmt)
        axarr[1].plot(datenums,
                      value_val,
                      marker='o',
                      linestyle='-',
                      color='green',
                      label='Value')

        axarr[1].legend(loc='upper left')
        #   plt.annotate('unknown transaction = %d \n pending transactions = %d' %(unknown_trans,pending_trans),xy=(0.7,0.05),xycoords='axes fraction',size=12)
        plt.show()
Example #28
0
    def _init_legend_box(self, handles, labels):
        """
        Initiallize the legend_box. The legend_box is an instance of
        the OffsetBox, which is packed with legend handles and
        texts. Once packed, their location is calculated during the
        drawing time.
        """

        fontsize = self.fontsize

        # legend_box is a HPacker, horizontally packed with
        # columns. Each column is a VPacker, vertically packed with
        # legend items. Each legend item is HPacker packed with
        # legend handleBox and labelBox. handleBox is an instance of
        # offsetbox.DrawingArea which contains legend handle. labelBox
        # is an instance of offsetbox.TextArea which contains legend
        # text.


        text_list = []  # the list of text instances
        handle_list = []  # the list of text instances

        label_prop = dict(verticalalignment='baseline',
                          horizontalalignment='left',
                          fontproperties=self.prop,
                          )

        labelboxes = []

        for l in labels:
            textbox = TextArea(l, textprops=label_prop,
                               multilinebaseline=True, minimumdescent=True)
            text_list.append(textbox._text)
            labelboxes.append(textbox)

        handleboxes = []


        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        height = self._approx_text_height() * 0.7
        descent = 0.

        # each handle needs to be drawn inside a box of (x, y, w, h) =
        # (0, -descent, width, height).  And their corrdinates should
        # be given in the display coordinates.

        # NOTE : the coordinates will be updated again in
        # _update_legend_box() method.

        # The transformation of each handle will be automatically set
        # to self.get_trasnform(). If the artist does not uses its
        # default trasnform (eg, Collections), you need to
        # manually set their transform to the self.get_transform().

        for handle in handles:
            if isinstance(handle, RegularPolyCollection):
                npoints = self.scatterpoints
            else:
                npoints = self.numpoints
            if npoints > 1:
                # we put some pad here to compensate the size of the
                # marker
                xdata = np.linspace(0.3*fontsize,
                                    (self.handlelength-0.3)*fontsize,
                                    npoints)
                xdata_marker = xdata
            elif npoints == 1:
                xdata = np.linspace(0, self.handlelength*fontsize, 2)
                xdata_marker = [0.5*self.handlelength*fontsize]

            if isinstance(handle, Line2D):
                ydata = ((height-descent)/2.)*np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)

                legline.update_from(handle)
                self._set_artist_props(legline) # after update
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                legline.set_drawstyle('default')
                legline.set_marker('None')

                handle_list.append(legline)

                legline_marker = Line2D(xdata_marker, ydata[:len(xdata_marker)])
                legline_marker.update_from(handle)
                self._set_artist_props(legline_marker)
                legline_marker.set_clip_box(None)
                legline_marker.set_clip_path(None)
                legline_marker.set_linestyle('None')
                # we don't want to add this to the return list because
                # the texts and handles are assumed to be in one-to-one
                # correpondence.
                legline._legmarker = legline_marker

            elif isinstance(handle, Patch):
                p = Rectangle(xy=(0., 0.),
                              width = self.handlelength*fontsize,
                              height=(height-descent),
                              )
                p.update_from(handle)
                self._set_artist_props(p)
                p.set_clip_box(None)
                p.set_clip_path(None)
                handle_list.append(p)
            elif isinstance(handle, LineCollection):
                ydata = ((height-descent)/2.)*np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)
                self._set_artist_props(legline)
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                lw = handle.get_linewidth()[0]
                dashes = handle.get_dashes()[0]
                color = handle.get_colors()[0]
                legline.set_color(color)
                legline.set_linewidth(lw)
                legline.set_dashes(dashes)
                handle_list.append(legline)

            elif isinstance(handle, RegularPolyCollection):

                #ydata = self._scatteryoffsets
                ydata = height*self._scatteryoffsets

                size_max, size_min = max(handle.get_sizes()),\
                                     min(handle.get_sizes())
                # we may need to scale these sizes by "markerscale"
                # attribute. But other handle types does not seem
                # to care about this attribute and it is currently ignored.
                if self.scatterpoints < 4:
                    sizes = [.5*(size_max+size_min), size_max,
                             size_min]
                else:
                    sizes = (size_max-size_min)*np.linspace(0,1,self.scatterpoints)+size_min

                p = type(handle)(handle.get_numsides(),
                                 rotation=handle.get_rotation(),
                                 sizes=sizes,
                                 offsets=zip(xdata_marker,ydata),
                                 transOffset=self.get_transform(),
                                 )

                p.update_from(handle)
                p.set_figure(self.figure)
                p.set_clip_box(None)
                p.set_clip_path(None)
                handle_list.append(p)

            else:
                handle_list.append(None)

            handlebox = DrawingArea(width=self.handlelength*fontsize,
                                    height=height,
                                    xdescent=0., ydescent=descent)

            handle = handle_list[-1]
            handlebox.add_artist(handle)
            if hasattr(handle, "_legmarker"):
                handlebox.add_artist(handle._legmarker)
            handleboxes.append(handlebox)


        # We calculate number of lows in each column. The first
        # (num_largecol) columns will have (nrows+1) rows, and remaing
        # (num_smallcol) columns will have (nrows) rows.
        nrows, num_largecol = divmod(len(handleboxes), self._ncol)
        num_smallcol = self._ncol-num_largecol

        # starting index of each column and number of rows in it.
        largecol = safezip(range(0, num_largecol*(nrows+1), (nrows+1)),
                           [nrows+1] * num_largecol)
        smallcol = safezip(range(num_largecol*(nrows+1), len(handleboxes), nrows),
                           [nrows] * num_smallcol)

        handle_label = safezip(handleboxes, labelboxes)
        columnbox = []
        for i0, di in largecol+smallcol:
            # pack handleBox and labelBox into itemBox
            itemBoxes = [HPacker(pad=0,
                                 sep=self.handletextpad*fontsize,
                                 children=[h, t], align="baseline")
                         for h, t in handle_label[i0:i0+di]]
            # minimumdescent=False for the text of the last row of the column
            itemBoxes[-1].get_children()[1].set_minimumdescent(False)

            # pack columnBox
            columnbox.append(VPacker(pad=0,
                                        sep=self.labelspacing*fontsize,
                                        align="baseline",
                                        children=itemBoxes))

        if self._mode == "expand":
            mode = "expand"
        else:
            mode = "fixed"

        sep = self.columnspacing*fontsize

        self._legend_box = HPacker(pad=self.borderpad*fontsize,
                                      sep=sep, align="baseline",
                                      mode=mode,
                                      children=columnbox)

        self._legend_box.set_figure(self.figure)

        self.texts = text_list
        self.legendHandles = handle_list
Example #29
0
class Legend(Artist):
    """
    Place a legend on the axes at location loc.  Labels are a
    sequence of strings and loc can be a string or an integer
    specifying the legend location

    The location codes are::

      'best'         : 0, (only implemented for axis legends)
      'upper right'  : 1,
      'upper left'   : 2,
      'lower left'   : 3,
      'lower right'  : 4,
      'right'        : 5,
      'center left'  : 6,
      'center right' : 7,
      'lower center' : 8,
      'upper center' : 9,
      'center'       : 10,

    loc can be a tuple of the noramilzed coordinate values with
    respect its parent.

    Return value is a sequence of text, line instances that make
    up the legend
    """


    codes = {'best'         : 0, # only implemented for axis legends
             'upper right'  : 1,
             'upper left'   : 2,
             'lower left'   : 3,
             'lower right'  : 4,
             'right'        : 5,
             'center left'  : 6,
             'center right' : 7,
             'lower center' : 8,
             'upper center' : 9,
             'center'       : 10,
             }


    zorder = 5
    def __str__(self):
        return "Legend"

    def __init__(self, parent, handles, labels,
                 loc = None,
                 numpoints = None,     # the number of points in the legend line
                 markerscale = None,   # the relative size of legend markers vs. original
                 scatterpoints = 3,    # TODO: may be an rcParam
                 scatteryoffsets=None,
                 prop = None,          # properties for the legend texts

                 # the following dimensions are in axes coords
                 pad = None,           # deprecated; use borderpad
                 labelsep = None,      # deprecated; use labelspacing
                 handlelen = None,     # deprecated; use handlelength
                 handletextsep = None, # deprecated; use handletextpad
                 axespad = None,       # deprecated; use borderaxespad

                 # spacing & pad defined as a fractionof the font-size
                 borderpad = None,     # the whitespace inside the legend border
                 labelspacing=None, #the vertical space between the legend entries
                 handlelength=None, # the length of the legend handles
                 handletextpad=None, # the pad between the legend handle and text
                 borderaxespad=None, # the pad between the axes and legend border
                 columnspacing=None, # spacing between columns

                 ncol=1, # number of columns
                 mode=None, # mode for horizontal distribution of columns. None, "expand"

                 fancybox=None, # True use a fancy box, false use a rounded box, none use rc
                 shadow = None,
                 ):
        """
        - *parent* : the artist that contains the legend
        - *handles* : a list of artists (lines, patches) to add to the legend
        - *labels* : a list of strings to label the legend

        Optional keyword arguments:

        ================   ==================================================================
        Keyword            Description
        ================   ==================================================================
        loc                a location code or a tuple of coordinates
        numpoints          the number of points in the legend line
        prop               the font property
        markerscale        the relative size of legend markers vs. original
        fancybox           if True, draw a frame with a round fancybox.  If None, use rc
        shadow             if True, draw a shadow behind legend
        scatteryoffsets    a list of yoffsets for scatter symbols in legend
        borderpad          the fractional whitespace inside the legend border
        labelspacing       the vertical space between the legend entries
        handlelength       the length of the legend handles
        handletextpad      the pad between the legend handle and text
        borderaxespad      the pad between the axes and legend border
        columnspacing      the spacing between columns
        ================   ==================================================================

The dimensions of pad and spacing are given as a fraction of the
fontsize. Values from rcParams will be used if None.
        """
        from matplotlib.axes import Axes     # local import only to avoid circularity
        from matplotlib.figure import Figure # local import only to avoid circularity

        Artist.__init__(self)

        if prop is None:
            self.prop=FontProperties(size=rcParams["legend.fontsize"])
        else:
            self.prop=prop
        self.fontsize = self.prop.get_size_in_points()

        propnames=['numpoints', 'markerscale', 'shadow', "columnspacing",
                   "scatterpoints"]

        localdict = locals()

        for name in propnames:
            if localdict[name] is None:
                value = rcParams["legend."+name]
            else:
                value = localdict[name]
            setattr(self, name, value)

        # Take care the deprecated keywords
        deprecated_kwds = {"pad":"borderpad",
                           "labelsep":"labelspacing",
                           "handlelen":"handlelength",
                           "handletextsep":"handletextpad",
                           "axespad":"borderaxespad"}

        # convert values of deprecated keywords (ginve in axes coords)
        # to new vaules in a fraction of the font size

        # conversion factor
        bbox = parent.bbox
        axessize_fontsize = min(bbox.width, bbox.height)/self.fontsize

        for k, v in deprecated_kwds.items():
            # use deprecated value if not None and if their newer
            # counter part is None.
            if localdict[k] is not None and localdict[v] is None:
                warnings.warn("Use '%s' instead of '%s'." % (v, k),
                              DeprecationWarning)
                setattr(self, v, localdict[k]*axessize_fontsize)
                continue

            # Otherwise, use new keywords
            if localdict[v] is None:
                setattr(self, v, rcParams["legend."+v])
            else:
                setattr(self, v, localdict[v])

        del localdict

        self._ncol = ncol

        if self.numpoints <= 0:
            raise ValueError("numpoints must be >= 0; it was %d"% numpoints)

        # introduce y-offset for handles of the scatter plot
        if scatteryoffsets is None:
            self._scatteryoffsets = np.array([3./8., 4./8., 2.5/8.])
        else:
            self._scatteryoffsets = np.asarray(scatteryoffsets)
        reps =  int(self.numpoints / len(self._scatteryoffsets)) + 1
        self._scatteryoffsets = np.tile(self._scatteryoffsets, reps)[:self.scatterpoints]

        # _legend_box is an OffsetBox instance that contains all
        # legend items and will be initialized from _init_legend_box()
        # method.
        self._legend_box = None

        if isinstance(parent,Axes):
            self.isaxes = True
            self.set_figure(parent.figure)
        elif isinstance(parent,Figure):
            self.isaxes = False
            self.set_figure(parent)
        else:
            raise TypeError("Legend needs either Axes or Figure as parent")
        self.parent = parent

        if loc is None:
            loc = rcParams["legend.loc"]
            if not self.isaxes and loc in [0,'best']:
                loc = 'upper right'
        if is_string_like(loc):
            if loc not in self.codes:
                if self.isaxes:
                    warnings.warn('Unrecognized location "%s". Falling back on "best"; '
                                  'valid locations are\n\t%s\n'
                                  % (loc, '\n\t'.join(self.codes.keys())))
                    loc = 0
                else:
                    warnings.warn('Unrecognized location "%s". Falling back on "upper right"; '
                                  'valid locations are\n\t%s\n'
                                   % (loc, '\n\t'.join(self.codes.keys())))
                    loc = 1
            else:
                loc = self.codes[loc]
        if not self.isaxes and loc == 0:
            warnings.warn('Automatic legend placement (loc="best") not implemented for figure legend. '
                          'Falling back on "upper right".')
            loc = 1

        self._loc = loc
        self._mode = mode

        # We use FancyBboxPatch to draw a legend frame. The location
        # and size of the box will be updated during the drawing time.
        self.legendPatch = FancyBboxPatch(
            xy=(0.0, 0.0), width=1., height=1.,
            facecolor='w', edgecolor='k',
            mutation_scale=self.fontsize,
            snap=True
            )

        # The width and height of the legendPatch will be set (in the
        # draw()) to the length that includes the padding. Thus we set
        # pad=0 here.
        if fancybox is None:
            fancybox = rcParams["legend.fancybox"]

        if fancybox == True:
            self.legendPatch.set_boxstyle("round",pad=0,
                                          rounding_size=0.2)
        else:
            self.legendPatch.set_boxstyle("square",pad=0)

        self._set_artist_props(self.legendPatch)

        self._drawFrame = True
        
        # init with null renderer
        self._init_legend_box(handles, labels)

        self._last_fontsize_points = self.fontsize


    def _set_artist_props(self, a):
        """
        set the boilerplate props for artists added to axes
        """
        a.set_figure(self.figure)

        for c in self.get_children():
            c.set_figure(self.figure)

        a.set_transform(self.get_transform())

    def _findoffset_best(self, width, height, xdescent, ydescent, renderer):
        "Heper function to locate the legend at its best position"
        ox, oy = self._find_best_position(width, height, renderer)
        return ox+xdescent, oy+ydescent

    def _findoffset_loc(self, width, height, xdescent, ydescent, renderer):
        "Heper function to locate the legend using the location code"

        if iterable(self._loc) and len(self._loc)==2:
            # when loc is a tuple of axes(or figure) coordinates.
            fx, fy = self._loc
            bbox = self.parent.bbox
            x, y = bbox.x0 + bbox.width * fx, bbox.y0 + bbox.height * fy
        else:
            bbox = Bbox.from_bounds(0, 0, width, height)
            x, y = self._get_anchored_bbox(self._loc, bbox, self.parent.bbox, renderer)

        return x+xdescent, y+ydescent

    def draw(self, renderer):
        "Draw everything that belongs to the legend"
        if not self.get_visible(): return

        self._update_legend_box(renderer)

        renderer.open_group('legend')

        # find_offset function will be provided to _legend_box and
        # _legend_box will draw itself at the location of the return
        # value of the find_offset.
        if self._loc == 0:
            _findoffset = self._findoffset_best
        else:
            _findoffset = self._findoffset_loc

        def findoffset(width, height, xdescent, ydescent):
            return _findoffset(width, height, xdescent, ydescent, renderer)
        
        self._legend_box.set_offset(findoffset)
        
        fontsize = renderer.points_to_pixels(self.fontsize)

        # if mode == fill, set the width of the legend_box to the
        # width of the paret (minus pads)
        if self._mode in ["expand"]:
            pad = 2*(self.borderaxespad+self.borderpad)*fontsize
            self._legend_box.set_width(self.parent.bbox.width-pad)

        if self._drawFrame:
            # update the location and size of the legend
            bbox = self._legend_box.get_window_extent(renderer)
            self.legendPatch.set_bounds(bbox.x0, bbox.y0,
                                        bbox.width, bbox.height)

            self.legendPatch.set_mutation_scale(fontsize)

            if self.shadow:
                shadow = Shadow(self.legendPatch, 2, -2)
                shadow.draw(renderer)

            self.legendPatch.draw(renderer)

        self._legend_box.draw(renderer)

        renderer.close_group('legend')


    def _approx_text_height(self, renderer=None):
        """
        Return the approximate height of the text. This is used to place
        the legend handle.
        """
        if renderer is None:
            return self.fontsize
        else:
            return renderer.points_to_pixels(self.fontsize)


    def _init_legend_box(self, handles, labels):
        """
        Initiallize the legend_box. The legend_box is an instance of
        the OffsetBox, which is packed with legend handles and
        texts. Once packed, their location is calculated during the
        drawing time.
        """

        fontsize = self.fontsize

        # legend_box is a HPacker, horizontally packed with
        # columns. Each column is a VPacker, vertically packed with
        # legend items. Each legend item is HPacker packed with
        # legend handleBox and labelBox. handleBox is an instance of
        # offsetbox.DrawingArea which contains legend handle. labelBox
        # is an instance of offsetbox.TextArea which contains legend
        # text.


        text_list = []  # the list of text instances
        handle_list = []  # the list of text instances

        label_prop = dict(verticalalignment='baseline',
                          horizontalalignment='left',
                          fontproperties=self.prop,
                          )

        labelboxes = []

        for l in labels:
            textbox = TextArea(l, textprops=label_prop,
                               multilinebaseline=True, minimumdescent=True)
            text_list.append(textbox._text)
            labelboxes.append(textbox)

        handleboxes = []


        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        height = self._approx_text_height() * 0.7
        descent = 0.

        # each handle needs to be drawn inside a box of (x, y, w, h) =
        # (0, -descent, width, height).  And their corrdinates should
        # be given in the display coordinates.

        # NOTE : the coordinates will be updated again in
        # _update_legend_box() method.

        # The transformation of each handle will be automatically set
        # to self.get_trasnform(). If the artist does not uses its
        # default trasnform (eg, Collections), you need to
        # manually set their transform to the self.get_transform().

        for handle in handles:
            if isinstance(handle, RegularPolyCollection):
                npoints = self.scatterpoints
            else:
                npoints = self.numpoints
            if npoints > 1:
                # we put some pad here to compensate the size of the
                # marker
                xdata = np.linspace(0.3*fontsize,
                                    (self.handlelength-0.3)*fontsize,
                                    npoints)
                xdata_marker = xdata
            elif npoints == 1:
                xdata = np.linspace(0, self.handlelength*fontsize, 2)
                xdata_marker = [0.5*self.handlelength*fontsize]

            if isinstance(handle, Line2D):
                ydata = ((height-descent)/2.)*np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)

                legline.update_from(handle)
                self._set_artist_props(legline) # after update
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                legline.set_drawstyle('default')
                legline.set_marker('None')

                handle_list.append(legline)

                legline_marker = Line2D(xdata_marker, ydata[:len(xdata_marker)])
                legline_marker.update_from(handle)
                self._set_artist_props(legline_marker)
                legline_marker.set_clip_box(None)
                legline_marker.set_clip_path(None)
                legline_marker.set_linestyle('None')
                # we don't want to add this to the return list because
                # the texts and handles are assumed to be in one-to-one
                # correpondence.
                legline._legmarker = legline_marker

            elif isinstance(handle, Patch):
                p = Rectangle(xy=(0., 0.),
                              width = self.handlelength*fontsize,
                              height=(height-descent),
                              )
                p.update_from(handle)
                self._set_artist_props(p)
                p.set_clip_box(None)
                p.set_clip_path(None)
                handle_list.append(p)
            elif isinstance(handle, LineCollection):
                ydata = ((height-descent)/2.)*np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)
                self._set_artist_props(legline)
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                lw = handle.get_linewidth()[0]
                dashes = handle.get_dashes()[0]
                color = handle.get_colors()[0]
                legline.set_color(color)
                legline.set_linewidth(lw)
                legline.set_dashes(dashes)
                handle_list.append(legline)

            elif isinstance(handle, RegularPolyCollection):

                #ydata = self._scatteryoffsets
                ydata = height*self._scatteryoffsets

                size_max, size_min = max(handle.get_sizes()),\
                                     min(handle.get_sizes())
                # we may need to scale these sizes by "markerscale"
                # attribute. But other handle types does not seem
                # to care about this attribute and it is currently ignored.
                if self.scatterpoints < 4:
                    sizes = [.5*(size_max+size_min), size_max,
                             size_min]
                else:
                    sizes = (size_max-size_min)*np.linspace(0,1,self.scatterpoints)+size_min

                p = type(handle)(handle.get_numsides(),
                                 rotation=handle.get_rotation(),
                                 sizes=sizes,
                                 offsets=zip(xdata_marker,ydata),
                                 transOffset=self.get_transform(),
                                 )

                p.update_from(handle)
                p.set_figure(self.figure)
                p.set_clip_box(None)
                p.set_clip_path(None)
                handle_list.append(p)

            else:
                handle_list.append(None)

            handlebox = DrawingArea(width=self.handlelength*fontsize,
                                    height=height,
                                    xdescent=0., ydescent=descent)

            handle = handle_list[-1]
            handlebox.add_artist(handle)
            if hasattr(handle, "_legmarker"):
                handlebox.add_artist(handle._legmarker)
            handleboxes.append(handlebox)


        # We calculate number of lows in each column. The first
        # (num_largecol) columns will have (nrows+1) rows, and remaing
        # (num_smallcol) columns will have (nrows) rows.
        nrows, num_largecol = divmod(len(handleboxes), self._ncol)
        num_smallcol = self._ncol-num_largecol

        # starting index of each column and number of rows in it.
        largecol = safezip(range(0, num_largecol*(nrows+1), (nrows+1)),
                           [nrows+1] * num_largecol)
        smallcol = safezip(range(num_largecol*(nrows+1), len(handleboxes), nrows),
                           [nrows] * num_smallcol)

        handle_label = safezip(handleboxes, labelboxes)
        columnbox = []
        for i0, di in largecol+smallcol:
            # pack handleBox and labelBox into itemBox
            itemBoxes = [HPacker(pad=0,
                                 sep=self.handletextpad*fontsize,
                                 children=[h, t], align="baseline")
                         for h, t in handle_label[i0:i0+di]]
            # minimumdescent=False for the text of the last row of the column
            itemBoxes[-1].get_children()[1].set_minimumdescent(False)

            # pack columnBox
            columnbox.append(VPacker(pad=0,
                                        sep=self.labelspacing*fontsize,
                                        align="baseline",
                                        children=itemBoxes))

        if self._mode == "expand":
            mode = "expand"
        else:
            mode = "fixed"

        sep = self.columnspacing*fontsize

        self._legend_box = HPacker(pad=self.borderpad*fontsize,
                                      sep=sep, align="baseline",
                                      mode=mode,
                                      children=columnbox)

        self._legend_box.set_figure(self.figure)

        self.texts = text_list
        self.legendHandles = handle_list




    def _update_legend_box(self, renderer):
        """
        Update the dimension of the legend_box. This is required
        becuase the paddings, the hadle size etc. depends on the dpi
        of the renderer.
        """

        # fontsize in points.
        fontsize = renderer.points_to_pixels(self.fontsize)

        if self._last_fontsize_points == fontsize:
            # no update is needed
            return

        # each handle needs to be drawn inside a box of
        # (x, y, w, h) = (0, -descent, width, height).
        # And their corrdinates should be given in the display coordinates.

        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        height = self._approx_text_height(renderer) * 0.7
        descent = 0.

        for handle in self.legendHandles:
            if isinstance(handle, RegularPolyCollection):
                npoints = self.scatterpoints
            else:
                npoints = self.numpoints
            if npoints > 1:
                # we put some pad here to compensate the size of the
                # marker
                xdata = np.linspace(0.3*fontsize,
                                    (self.handlelength-0.3)*fontsize,
                                    npoints)
                xdata_marker = xdata
            elif npoints == 1:
                xdata = np.linspace(0, self.handlelength*fontsize, 2)
                xdata_marker = [0.5*self.handlelength*fontsize]

            if isinstance(handle, Line2D):
                legline = handle
                ydata = ((height-descent)/2.)*np.ones(xdata.shape, float)
                legline.set_data(xdata, ydata)

                legline_marker = legline._legmarker
                legline_marker.set_data(xdata_marker, ydata[:len(xdata_marker)])

            elif isinstance(handle, Patch):
                p = handle
                p.set_bounds(0., 0.,
                             self.handlelength*fontsize,
                             (height-descent),
                             )

            elif isinstance(handle, RegularPolyCollection):

                p = handle
                ydata = height*self._scatteryoffsets
                p.set_offsets(zip(xdata_marker,ydata))


        # correction factor
        cor = fontsize / self._last_fontsize_points

        # helper function to iterate over all children
        def all_children(parent):
            yield parent
            for c in parent.get_children():
                for cc in all_children(c): yield cc


        #now update paddings
        for box in all_children(self._legend_box):
            if isinstance(box, PackerBase):
                box.pad = box.pad * cor
                box.sep = box.sep * cor

            elif isinstance(box, DrawingArea):
                box.width = self.handlelength*fontsize
                box.height = height
                box.xdescent = 0.
                box.ydescent=descent

        self._last_fontsize_points = fontsize


    def _auto_legend_data(self):
        """
        Returns list of vertices and extents covered by the plot.

        Returns a two long list.

        First element is a list of (x, y) vertices (in
        display-coordinates) covered by all the lines and line
        collections, in the legend's handles.

        Second element is a list of bounding boxes for all the patches in
        the legend's handles.
        """

        assert self.isaxes # should always hold because function is only called internally

        ax = self.parent
        vertices = []
        bboxes = []
        lines = []

        for handle in ax.lines:
            assert isinstance(handle, Line2D)
            path = handle.get_path()
            trans = handle.get_transform()
            tpath = trans.transform_path(path)
            lines.append(tpath)

        for handle in ax.patches:
            assert isinstance(handle, Patch)

            if isinstance(handle, Rectangle):
                transform = handle.get_data_transform()
                bboxes.append(handle.get_bbox().transformed(transform))
            else:
                transform = handle.get_transform()
                bboxes.append(handle.get_path().get_extents(transform))

        return [vertices, bboxes, lines]

    def draw_frame(self, b):
        'b is a boolean.  Set draw frame to b'
        self._drawFrame = b

    def get_children(self):
        'return a list of child artists'
        children = []
        if self._legend_box:
            children.append(self._legend_box)
        return children

    def get_frame(self):
        'return the Rectangle instance used to frame the legend'
        return self.legendPatch

    def get_lines(self):
        'return a list of lines.Line2D instances in the legend'
        return [h for h in self.legendHandles if isinstance(h, Line2D)]

    def get_patches(self):
        'return a list of patch instances in the legend'
        return silent_list('Patch', [h for h in self.legendHandles if isinstance(h, Patch)])

    def get_texts(self):
        'return a list of text.Text instance in the legend'
        return silent_list('Text', self.texts)

    def get_window_extent(self):
        'return a extent of the the legend'
        return self.legendPatch.get_window_extent()


    def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer):
        """
        Place the *bbox* inside the *parentbbox* according to a given
        location code. Return the (x,y) coordinate of the bbox.

        - loc: a location code in range(1, 11).
          This corresponds to the possible values for self._loc, excluding "best".

        - bbox: bbox to be placed, display coodinate units.
        - parentbbox: a parent box which will contain the bbox. In
            display coordinates.
        """
        assert loc in range(1,11) # called only internally

        BEST, UR, UL, LL, LR, R, CL, CR, LC, UC, C = range(11)

        anchor_coefs={UR:"NE",
                      UL:"NW",
                      LL:"SW",
                      LR:"SE",
                      R:"E",
                      CL:"W",
                      CR:"E",
                      LC:"S",
                      UC:"N",
                      C:"C"}

        c = anchor_coefs[loc]
        
        fontsize = renderer.points_to_pixels(self.fontsize)
        container = parentbbox.padded(-(self.borderaxespad) * fontsize)
        anchored_box = bbox.anchored(c, container=container)
        return anchored_box.x0, anchored_box.y0


    def _find_best_position(self, width, height, renderer, consider=None):
        """
        Determine the best location to place the legend.

        `consider` is a list of (x, y) pairs to consider as a potential
        lower-left corner of the legend. All are display coords.
        """

        assert self.isaxes # should always hold because function is only called internally

        verts, bboxes, lines = self._auto_legend_data()

        bbox = Bbox.from_bounds(0, 0, width, height)
        consider = [self._get_anchored_bbox(x, bbox, self.parent.bbox, renderer) for x in range(1, len(self.codes))]

        #tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y()

        candidates = []
        for l, b in consider:
            legendBox = Bbox.from_bounds(l, b, width, height)
            badness = 0
            badness = legendBox.count_contains(verts)
            badness += legendBox.count_overlaps(bboxes)
            for line in lines:
                if line.intersects_bbox(legendBox):
                    badness += 1

            ox, oy = l, b
            if badness == 0:
                return ox, oy

            candidates.append((badness, (l, b)))

        # rather than use min() or list.sort(), do this so that we are assured
        # that in the case of two equal badnesses, the one first considered is
        # returned.
        # NOTE: list.sort() is stable.But leave as it is for now. -JJL
        minCandidate = candidates[0]
        for candidate in candidates:
            if candidate[0] < minCandidate[0]:
                minCandidate = candidate

        ox, oy = minCandidate[1]

        return ox, oy
Example #30
0
class Legend(Artist):
    """
    Place a legend on the axes at location loc.  Labels are a
    sequence of strings and loc can be a string or an integer
    specifying the legend location

    The location codes are::

      'best'         : 0, (only implemented for axis legends)
      'upper right'  : 1,
      'upper left'   : 2,
      'lower left'   : 3,
      'lower right'  : 4,
      'right'        : 5,
      'center left'  : 6,
      'center right' : 7,
      'lower center' : 8,
      'upper center' : 9,
      'center'       : 10,

    loc can be a tuple of the noramilzed coordinate values with
    respect its parent.

    Return value is a sequence of text, line instances that make
    up the legend
    """

    codes = {
        'best': 0,  # only implemented for axis legends
        'upper right': 1,
        'upper left': 2,
        'lower left': 3,
        'lower right': 4,
        'right': 5,
        'center left': 6,
        'center right': 7,
        'lower center': 8,
        'upper center': 9,
        'center': 10,
    }

    zorder = 5

    def __str__(self):
        return "Legend"

    def __init__(
        self,
        parent,
        handles,
        labels,
        loc=None,
        numpoints=None,  # the number of points in the legend line
        markerscale=None,  # the relative size of legend markers vs. original
        scatterpoints=3,  # TODO: may be an rcParam
        scatteryoffsets=None,
        prop=None,  # properties for the legend texts

        # the following dimensions are in axes coords
        pad=None,  # deprecated; use borderpad
        labelsep=None,  # deprecated; use labelspacing
        handlelen=None,  # deprecated; use handlelength
        handletextsep=None,  # deprecated; use handletextpad
        axespad=None,  # deprecated; use borderaxespad

        # spacing & pad defined as a fraction of the font-size
        borderpad=None,  # the whitespace inside the legend border
        labelspacing=None,  #the vertical space between the legend entries
        handlelength=None,  # the length of the legend handles
        handletextpad=None,  # the pad between the legend handle and text
        borderaxespad=None,  # the pad between the axes and legend border
        columnspacing=None,  # spacing between columns
        ncol=1,  # number of columns
        mode=None,  # mode for horizontal distribution of columns. None, "expand"
        fancybox=None,  # True use a fancy box, false use a rounded box, none use rc
        shadow=None,
    ):
        """
        - *parent* : the artist that contains the legend
        - *handles* : a list of artists (lines, patches) to add to the legend
        - *labels* : a list of strings to label the legend

        Optional keyword arguments:

        ================   ==================================================================
        Keyword            Description
        ================   ==================================================================
        loc                a location code or a tuple of coordinates
        numpoints          the number of points in the legend line
        prop               the font property
        markerscale        the relative size of legend markers vs. original
        fancybox           if True, draw a frame with a round fancybox.  If None, use rc
        shadow             if True, draw a shadow behind legend
        scatteryoffsets    a list of yoffsets for scatter symbols in legend
        borderpad          the fractional whitespace inside the legend border
        labelspacing       the vertical space between the legend entries
        handlelength       the length of the legend handles
        handletextpad      the pad between the legend handle and text
        borderaxespad      the pad between the axes and legend border
        columnspacing      the spacing between columns
        ================   ==================================================================

The dimensions of pad and spacing are given as a fraction of the
fontsize. Values from rcParams will be used if None.
        """
        from matplotlib.axes import Axes  # local import only to avoid circularity
        from matplotlib.figure import Figure  # local import only to avoid circularity

        Artist.__init__(self)

        if prop is None:
            self.prop = FontProperties(size=rcParams["legend.fontsize"])
        else:
            self.prop = prop
        self.fontsize = self.prop.get_size_in_points()

        propnames = [
            'numpoints', 'markerscale', 'shadow', "columnspacing",
            "scatterpoints"
        ]

        localdict = locals()

        for name in propnames:
            if localdict[name] is None:
                value = rcParams["legend." + name]
            else:
                value = localdict[name]
            setattr(self, name, value)

        # Take care the deprecated keywords
        deprecated_kwds = {
            "pad": "borderpad",
            "labelsep": "labelspacing",
            "handlelen": "handlelength",
            "handletextsep": "handletextpad",
            "axespad": "borderaxespad"
        }

        # convert values of deprecated keywords (ginve in axes coords)
        # to new vaules in a fraction of the font size

        # conversion factor
        bbox = parent.bbox
        axessize_fontsize = min(bbox.width, bbox.height) / self.fontsize

        for k, v in deprecated_kwds.items():
            # use deprecated value if not None and if their newer
            # counter part is None.
            if localdict[k] is not None and localdict[v] is None:
                warnings.warn("Use '%s' instead of '%s'." % (v, k),
                              DeprecationWarning)
                setattr(self, v, localdict[k] * axessize_fontsize)
                continue

            # Otherwise, use new keywords
            if localdict[v] is None:
                setattr(self, v, rcParams["legend." + v])
            else:
                setattr(self, v, localdict[v])

        del localdict

        self._ncol = ncol

        if self.numpoints <= 0:
            raise ValueError("numpoints must be >= 0; it was %d" % numpoints)

        # introduce y-offset for handles of the scatter plot
        if scatteryoffsets is None:
            self._scatteryoffsets = np.array([3. / 8., 4. / 8., 2.5 / 8.])
        else:
            self._scatteryoffsets = np.asarray(scatteryoffsets)
        reps = int(self.numpoints / len(self._scatteryoffsets)) + 1
        self._scatteryoffsets = np.tile(self._scatteryoffsets,
                                        reps)[:self.scatterpoints]

        # _legend_box is an OffsetBox instance that contains all
        # legend items and will be initialized from _init_legend_box()
        # method.
        self._legend_box = None

        if isinstance(parent, Axes):
            self.isaxes = True
            self.set_figure(parent.figure)
        elif isinstance(parent, Figure):
            self.isaxes = False
            self.set_figure(parent)
        else:
            raise TypeError("Legend needs either Axes or Figure as parent")
        self.parent = parent

        if loc is None:
            loc = rcParams["legend.loc"]
            if not self.isaxes and loc in [0, 'best']:
                loc = 'upper right'
        if is_string_like(loc):
            if loc not in self.codes:
                if self.isaxes:
                    warnings.warn(
                        'Unrecognized location "%s". Falling back on "best"; '
                        'valid locations are\n\t%s\n' %
                        (loc, '\n\t'.join(self.codes.keys())))
                    loc = 0
                else:
                    warnings.warn(
                        'Unrecognized location "%s". Falling back on "upper right"; '
                        'valid locations are\n\t%s\n' %
                        (loc, '\n\t'.join(self.codes.keys())))
                    loc = 1
            else:
                loc = self.codes[loc]
        if not self.isaxes and loc == 0:
            warnings.warn(
                'Automatic legend placement (loc="best") not implemented for figure legend. '
                'Falling back on "upper right".')
            loc = 1

        self._loc = loc
        self._mode = mode

        # We use FancyBboxPatch to draw a legend frame. The location
        # and size of the box will be updated during the drawing time.
        self.legendPatch = FancyBboxPatch(xy=(0.0, 0.0),
                                          width=1.,
                                          height=1.,
                                          facecolor='w',
                                          edgecolor='k',
                                          mutation_scale=self.fontsize,
                                          snap=True)

        # The width and height of the legendPatch will be set (in the
        # draw()) to the length that includes the padding. Thus we set
        # pad=0 here.
        if fancybox is None:
            fancybox = rcParams["legend.fancybox"]

        if fancybox == True:
            self.legendPatch.set_boxstyle("round", pad=0, rounding_size=0.2)
        else:
            self.legendPatch.set_boxstyle("square", pad=0)

        self._set_artist_props(self.legendPatch)

        self._drawFrame = True

        # init with null renderer
        self._init_legend_box(handles, labels)

        self._last_fontsize_points = self.fontsize

    def _set_artist_props(self, a):
        """
        set the boilerplate props for artists added to axes
        """
        a.set_figure(self.figure)

        for c in self.get_children():
            c.set_figure(self.figure)

        a.set_transform(self.get_transform())

    def _findoffset_best(self, width, height, xdescent, ydescent, renderer):
        "Heper function to locate the legend at its best position"
        ox, oy = self._find_best_position(width, height, renderer)
        return ox + xdescent, oy + ydescent

    def _findoffset_loc(self, width, height, xdescent, ydescent, renderer):
        "Heper function to locate the legend using the location code"

        if iterable(self._loc) and len(self._loc) == 2:
            # when loc is a tuple of axes(or figure) coordinates.
            fx, fy = self._loc
            bbox = self.parent.bbox
            x, y = bbox.x0 + bbox.width * fx, bbox.y0 + bbox.height * fy
        else:
            bbox = Bbox.from_bounds(0, 0, width, height)
            x, y = self._get_anchored_bbox(self._loc, bbox, self.parent.bbox,
                                           renderer)

        return x + xdescent, y + ydescent

    def draw(self, renderer):
        "Draw everything that belongs to the legend"
        if not self.get_visible(): return

        self._update_legend_box(renderer)

        renderer.open_group('legend')

        # find_offset function will be provided to _legend_box and
        # _legend_box will draw itself at the location of the return
        # value of the find_offset.
        if self._loc == 0:
            _findoffset = self._findoffset_best
        else:
            _findoffset = self._findoffset_loc

        def findoffset(width, height, xdescent, ydescent):
            return _findoffset(width, height, xdescent, ydescent, renderer)

        self._legend_box.set_offset(findoffset)

        fontsize = renderer.points_to_pixels(self.fontsize)

        # if mode == fill, set the width of the legend_box to the
        # width of the paret (minus pads)
        if self._mode in ["expand"]:
            pad = 2 * (self.borderaxespad + self.borderpad) * fontsize
            self._legend_box.set_width(self.parent.bbox.width - pad)

        if self._drawFrame:
            # update the location and size of the legend
            bbox = self._legend_box.get_window_extent(renderer)
            self.legendPatch.set_bounds(bbox.x0, bbox.y0, bbox.width,
                                        bbox.height)

            self.legendPatch.set_mutation_scale(fontsize)

            if self.shadow:
                shadow = Shadow(self.legendPatch, 2, -2)
                shadow.draw(renderer)

            self.legendPatch.draw(renderer)

        self._legend_box.draw(renderer)

        renderer.close_group('legend')

    def _approx_text_height(self, renderer=None):
        """
        Return the approximate height of the text. This is used to place
        the legend handle.
        """
        if renderer is None:
            return self.fontsize
        else:
            return renderer.points_to_pixels(self.fontsize)

    def _init_legend_box(self, handles, labels):
        """
        Initiallize the legend_box. The legend_box is an instance of
        the OffsetBox, which is packed with legend handles and
        texts. Once packed, their location is calculated during the
        drawing time.
        """

        fontsize = self.fontsize

        # legend_box is a HPacker, horizontally packed with
        # columns. Each column is a VPacker, vertically packed with
        # legend items. Each legend item is HPacker packed with
        # legend handleBox and labelBox. handleBox is an instance of
        # offsetbox.DrawingArea which contains legend handle. labelBox
        # is an instance of offsetbox.TextArea which contains legend
        # text.

        text_list = []  # the list of text instances
        handle_list = []  # the list of text instances

        label_prop = dict(
            verticalalignment='baseline',
            horizontalalignment='left',
            fontproperties=self.prop,
        )

        labelboxes = []

        for l in labels:
            textbox = TextArea(l,
                               textprops=label_prop,
                               multilinebaseline=True,
                               minimumdescent=True)
            text_list.append(textbox._text)
            labelboxes.append(textbox)

        handleboxes = []

        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        height = self._approx_text_height() * 0.7
        descent = 0.

        # each handle needs to be drawn inside a box of (x, y, w, h) =
        # (0, -descent, width, height).  And their corrdinates should
        # be given in the display coordinates.

        # NOTE : the coordinates will be updated again in
        # _update_legend_box() method.

        # The transformation of each handle will be automatically set
        # to self.get_trasnform(). If the artist does not uses its
        # default trasnform (eg, Collections), you need to
        # manually set their transform to the self.get_transform().

        for handle in handles:
            if isinstance(handle, RegularPolyCollection):
                npoints = self.scatterpoints
            else:
                npoints = self.numpoints
            if npoints > 1:
                # we put some pad here to compensate the size of the
                # marker
                xdata = np.linspace(0.3 * fontsize,
                                    (self.handlelength - 0.3) * fontsize,
                                    npoints)
                xdata_marker = xdata
            elif npoints == 1:
                xdata = np.linspace(0, self.handlelength * fontsize, 2)
                xdata_marker = [0.5 * self.handlelength * fontsize]

            if isinstance(handle, Line2D):
                ydata = ((height - descent) / 2.) * np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)

                legline.update_from(handle)
                self._set_artist_props(legline)  # after update
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                legline.set_drawstyle('default')
                legline.set_marker('None')

                handle_list.append(legline)

                legline_marker = Line2D(xdata_marker,
                                        ydata[:len(xdata_marker)])
                legline_marker.update_from(handle)
                self._set_artist_props(legline_marker)
                legline_marker.set_clip_box(None)
                legline_marker.set_clip_path(None)
                legline_marker.set_linestyle('None')
                # we don't want to add this to the return list because
                # the texts and handles are assumed to be in one-to-one
                # correpondence.
                legline._legmarker = legline_marker

            elif isinstance(handle, Patch):
                p = Rectangle(
                    xy=(0., 0.),
                    width=self.handlelength * fontsize,
                    height=(height - descent),
                )
                p.update_from(handle)
                self._set_artist_props(p)
                p.set_clip_box(None)
                p.set_clip_path(None)
                handle_list.append(p)
            elif isinstance(handle, LineCollection):
                ydata = ((height - descent) / 2.) * np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)
                self._set_artist_props(legline)
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                lw = handle.get_linewidth()[0]
                dashes = handle.get_dashes()[0]
                color = handle.get_colors()[0]
                legline.set_color(color)
                legline.set_linewidth(lw)
                legline.set_dashes(dashes)
                handle_list.append(legline)

            elif isinstance(handle, RegularPolyCollection):

                #ydata = self._scatteryoffsets
                ydata = height * self._scatteryoffsets

                size_max, size_min = max(handle.get_sizes()),\
                                     min(handle.get_sizes())
                # we may need to scale these sizes by "markerscale"
                # attribute. But other handle types does not seem
                # to care about this attribute and it is currently ignored.
                if self.scatterpoints < 4:
                    sizes = [.5 * (size_max + size_min), size_max, size_min]
                else:
                    sizes = (size_max - size_min) * np.linspace(
                        0, 1, self.scatterpoints) + size_min

                p = type(handle)(
                    handle.get_numsides(),
                    rotation=handle.get_rotation(),
                    sizes=sizes,
                    offsets=zip(xdata_marker, ydata),
                    transOffset=self.get_transform(),
                )

                p.update_from(handle)
                p.set_figure(self.figure)
                p.set_clip_box(None)
                p.set_clip_path(None)
                handle_list.append(p)

            else:
                handle_list.append(None)

            handlebox = DrawingArea(width=self.handlelength * fontsize,
                                    height=height,
                                    xdescent=0.,
                                    ydescent=descent)

            handle = handle_list[-1]
            handlebox.add_artist(handle)
            if hasattr(handle, "_legmarker"):
                handlebox.add_artist(handle._legmarker)
            handleboxes.append(handlebox)

        # We calculate number of lows in each column. The first
        # (num_largecol) columns will have (nrows+1) rows, and remaing
        # (num_smallcol) columns will have (nrows) rows.
        nrows, num_largecol = divmod(len(handleboxes), self._ncol)
        num_smallcol = self._ncol - num_largecol

        # starting index of each column and number of rows in it.
        largecol = safezip(range(0, num_largecol * (nrows + 1), (nrows + 1)),
                           [nrows + 1] * num_largecol)
        smallcol = safezip(
            range(num_largecol * (nrows + 1), len(handleboxes), nrows),
            [nrows] * num_smallcol)

        handle_label = safezip(handleboxes, labelboxes)
        columnbox = []
        for i0, di in largecol + smallcol:
            # pack handleBox and labelBox into itemBox
            itemBoxes = [
                HPacker(pad=0,
                        sep=self.handletextpad * fontsize,
                        children=[h, t],
                        align="baseline") for h, t in handle_label[i0:i0 + di]
            ]
            # minimumdescent=False for the text of the last row of the column
            itemBoxes[-1].get_children()[1].set_minimumdescent(False)

            # pack columnBox
            columnbox.append(
                VPacker(pad=0,
                        sep=self.labelspacing * fontsize,
                        align="baseline",
                        children=itemBoxes))

        if self._mode == "expand":
            mode = "expand"
        else:
            mode = "fixed"

        sep = self.columnspacing * fontsize

        self._legend_box = HPacker(pad=self.borderpad * fontsize,
                                   sep=sep,
                                   align="baseline",
                                   mode=mode,
                                   children=columnbox)

        self._legend_box.set_figure(self.figure)

        self.texts = text_list
        self.legendHandles = handle_list

    def _update_legend_box(self, renderer):
        """
        Update the dimension of the legend_box. This is required
        becuase the paddings, the hadle size etc. depends on the dpi
        of the renderer.
        """

        # fontsize in points.
        fontsize = renderer.points_to_pixels(self.fontsize)

        if self._last_fontsize_points == fontsize:
            # no update is needed
            return

        # each handle needs to be drawn inside a box of
        # (x, y, w, h) = (0, -descent, width, height).
        # And their corrdinates should be given in the display coordinates.

        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        height = self._approx_text_height(renderer) * 0.7
        descent = 0.

        for handle in self.legendHandles:
            if isinstance(handle, RegularPolyCollection):
                npoints = self.scatterpoints
            else:
                npoints = self.numpoints
            if npoints > 1:
                # we put some pad here to compensate the size of the
                # marker
                xdata = np.linspace(0.3 * fontsize,
                                    (self.handlelength - 0.3) * fontsize,
                                    npoints)
                xdata_marker = xdata
            elif npoints == 1:
                xdata = np.linspace(0, self.handlelength * fontsize, 2)
                xdata_marker = [0.5 * self.handlelength * fontsize]

            if isinstance(handle, Line2D):
                legline = handle
                ydata = ((height - descent) / 2.) * np.ones(xdata.shape, float)
                legline.set_data(xdata, ydata)

                # if a line collection is added, the legmarker attr is
                # not set so we don't need to handle it
                if hasattr(handle, "_legmarker"):
                    legline_marker = legline._legmarker
                    legline_marker.set_data(xdata_marker,
                                            ydata[:len(xdata_marker)])

            elif isinstance(handle, Patch):
                p = handle
                p.set_bounds(
                    0.,
                    0.,
                    self.handlelength * fontsize,
                    (height - descent),
                )

            elif isinstance(handle, RegularPolyCollection):

                p = handle
                ydata = height * self._scatteryoffsets
                p.set_offsets(zip(xdata_marker, ydata))

        # correction factor
        cor = fontsize / self._last_fontsize_points

        # helper function to iterate over all children
        def all_children(parent):
            yield parent
            for c in parent.get_children():
                for cc in all_children(c):
                    yield cc

        #now update paddings
        for box in all_children(self._legend_box):
            if isinstance(box, PackerBase):
                box.pad = box.pad * cor
                box.sep = box.sep * cor

            elif isinstance(box, DrawingArea):
                box.width = self.handlelength * fontsize
                box.height = height
                box.xdescent = 0.
                box.ydescent = descent

        self._last_fontsize_points = fontsize

    def _auto_legend_data(self):
        """
        Returns list of vertices and extents covered by the plot.

        Returns a two long list.

        First element is a list of (x, y) vertices (in
        display-coordinates) covered by all the lines and line
        collections, in the legend's handles.

        Second element is a list of bounding boxes for all the patches in
        the legend's handles.
        """

        assert self.isaxes  # should always hold because function is only called internally

        ax = self.parent
        vertices = []
        bboxes = []
        lines = []

        for handle in ax.lines:
            assert isinstance(handle, Line2D)
            path = handle.get_path()
            trans = handle.get_transform()
            tpath = trans.transform_path(path)
            lines.append(tpath)

        for handle in ax.patches:
            assert isinstance(handle, Patch)

            if isinstance(handle, Rectangle):
                transform = handle.get_data_transform()
                bboxes.append(handle.get_bbox().transformed(transform))
            else:
                transform = handle.get_transform()
                bboxes.append(handle.get_path().get_extents(transform))

        return [vertices, bboxes, lines]

    def draw_frame(self, b):
        'b is a boolean.  Set draw frame to b'
        self._drawFrame = b

    def get_children(self):
        'return a list of child artists'
        children = []
        if self._legend_box:
            children.append(self._legend_box)
        return children

    def get_frame(self):
        'return the Rectangle instance used to frame the legend'
        return self.legendPatch

    def get_lines(self):
        'return a list of lines.Line2D instances in the legend'
        return [h for h in self.legendHandles if isinstance(h, Line2D)]

    def get_patches(self):
        'return a list of patch instances in the legend'
        return silent_list(
            'Patch', [h for h in self.legendHandles if isinstance(h, Patch)])

    def get_texts(self):
        'return a list of text.Text instance in the legend'
        return silent_list('Text', self.texts)

    def get_window_extent(self):
        'return a extent of the the legend'
        return self.legendPatch.get_window_extent()

    def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer):
        """
        Place the *bbox* inside the *parentbbox* according to a given
        location code. Return the (x,y) coordinate of the bbox.

        - loc: a location code in range(1, 11).
          This corresponds to the possible values for self._loc, excluding "best".

        - bbox: bbox to be placed, display coodinate units.
        - parentbbox: a parent box which will contain the bbox. In
            display coordinates.
        """
        assert loc in range(1, 11)  # called only internally

        BEST, UR, UL, LL, LR, R, CL, CR, LC, UC, C = range(11)

        anchor_coefs = {
            UR: "NE",
            UL: "NW",
            LL: "SW",
            LR: "SE",
            R: "E",
            CL: "W",
            CR: "E",
            LC: "S",
            UC: "N",
            C: "C"
        }

        c = anchor_coefs[loc]

        fontsize = renderer.points_to_pixels(self.fontsize)
        container = parentbbox.padded(-(self.borderaxespad) * fontsize)
        anchored_box = bbox.anchored(c, container=container)
        return anchored_box.x0, anchored_box.y0

    def _find_best_position(self, width, height, renderer, consider=None):
        """
        Determine the best location to place the legend.

        `consider` is a list of (x, y) pairs to consider as a potential
        lower-left corner of the legend. All are display coords.
        """

        assert self.isaxes  # should always hold because function is only called internally

        verts, bboxes, lines = self._auto_legend_data()

        bbox = Bbox.from_bounds(0, 0, width, height)
        consider = [
            self._get_anchored_bbox(x, bbox, self.parent.bbox, renderer)
            for x in range(1, len(self.codes))
        ]

        #tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y()

        candidates = []
        for l, b in consider:
            legendBox = Bbox.from_bounds(l, b, width, height)
            badness = 0
            badness = legendBox.count_contains(verts)
            badness += legendBox.count_overlaps(bboxes)
            for line in lines:
                if line.intersects_bbox(legendBox):
                    badness += 1

            ox, oy = l, b
            if badness == 0:
                return ox, oy

            candidates.append((badness, (l, b)))

        # rather than use min() or list.sort(), do this so that we are assured
        # that in the case of two equal badnesses, the one first considered is
        # returned.
        # NOTE: list.sort() is stable.But leave as it is for now. -JJL
        minCandidate = candidates[0]
        for candidate in candidates:
            if candidate[0] < minCandidate[0]:
                minCandidate = candidate

        ox, oy = minCandidate[1]

        return ox, oy
Example #31
0
    def _init_legend_box(self, handles, labels):
        """
        Initiallize the legend_box. The legend_box is an instance of
        the OffsetBox, which is packed with legend handles and
        texts. Once packed, their location is calculated during the
        drawing time.
        """

        fontsize = self.fontsize

        # legend_box is a HPacker, horizontally packed with
        # columns. Each column is a VPacker, vertically packed with
        # legend items. Each legend item is HPacker packed with
        # legend handleBox and labelBox. handleBox is an instance of
        # offsetbox.DrawingArea which contains legend handle. labelBox
        # is an instance of offsetbox.TextArea which contains legend
        # text.

        text_list = []  # the list of text instances
        handle_list = []  # the list of text instances

        label_prop = dict(
            verticalalignment='baseline',
            horizontalalignment='left',
            fontproperties=self.prop,
        )

        labelboxes = []

        for l in labels:
            textbox = TextArea(l,
                               textprops=label_prop,
                               multilinebaseline=True,
                               minimumdescent=True)
            text_list.append(textbox._text)
            labelboxes.append(textbox)

        handleboxes = []

        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        height = self._approx_text_height() * 0.7
        descent = 0.

        # each handle needs to be drawn inside a box of (x, y, w, h) =
        # (0, -descent, width, height).  And their corrdinates should
        # be given in the display coordinates.

        # NOTE : the coordinates will be updated again in
        # _update_legend_box() method.

        # The transformation of each handle will be automatically set
        # to self.get_trasnform(). If the artist does not uses its
        # default trasnform (eg, Collections), you need to
        # manually set their transform to the self.get_transform().

        for handle in handles:
            if isinstance(handle, RegularPolyCollection):
                npoints = self.scatterpoints
            else:
                npoints = self.numpoints
            if npoints > 1:
                # we put some pad here to compensate the size of the
                # marker
                xdata = np.linspace(0.3 * fontsize,
                                    (self.handlelength - 0.3) * fontsize,
                                    npoints)
                xdata_marker = xdata
            elif npoints == 1:
                xdata = np.linspace(0, self.handlelength * fontsize, 2)
                xdata_marker = [0.5 * self.handlelength * fontsize]

            if isinstance(handle, Line2D):
                ydata = ((height - descent) / 2.) * np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)

                legline.update_from(handle)
                self._set_artist_props(legline)  # after update
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                legline.set_drawstyle('default')
                legline.set_marker('None')

                handle_list.append(legline)

                legline_marker = Line2D(xdata_marker,
                                        ydata[:len(xdata_marker)])
                legline_marker.update_from(handle)
                self._set_artist_props(legline_marker)
                legline_marker.set_clip_box(None)
                legline_marker.set_clip_path(None)
                legline_marker.set_linestyle('None')
                # we don't want to add this to the return list because
                # the texts and handles are assumed to be in one-to-one
                # correpondence.
                legline._legmarker = legline_marker

            elif isinstance(handle, Patch):
                p = Rectangle(
                    xy=(0., 0.),
                    width=self.handlelength * fontsize,
                    height=(height - descent),
                )
                p.update_from(handle)
                self._set_artist_props(p)
                p.set_clip_box(None)
                p.set_clip_path(None)
                handle_list.append(p)
            elif isinstance(handle, LineCollection):
                ydata = ((height - descent) / 2.) * np.ones(xdata.shape, float)
                legline = Line2D(xdata, ydata)
                self._set_artist_props(legline)
                legline.set_clip_box(None)
                legline.set_clip_path(None)
                lw = handle.get_linewidth()[0]
                dashes = handle.get_dashes()[0]
                color = handle.get_colors()[0]
                legline.set_color(color)
                legline.set_linewidth(lw)
                legline.set_dashes(dashes)
                handle_list.append(legline)

            elif isinstance(handle, RegularPolyCollection):

                #ydata = self._scatteryoffsets
                ydata = height * self._scatteryoffsets

                size_max, size_min = max(handle.get_sizes()),\
                                     min(handle.get_sizes())
                # we may need to scale these sizes by "markerscale"
                # attribute. But other handle types does not seem
                # to care about this attribute and it is currently ignored.
                if self.scatterpoints < 4:
                    sizes = [.5 * (size_max + size_min), size_max, size_min]
                else:
                    sizes = (size_max - size_min) * np.linspace(
                        0, 1, self.scatterpoints) + size_min

                p = type(handle)(
                    handle.get_numsides(),
                    rotation=handle.get_rotation(),
                    sizes=sizes,
                    offsets=zip(xdata_marker, ydata),
                    transOffset=self.get_transform(),
                )

                p.update_from(handle)
                p.set_figure(self.figure)
                p.set_clip_box(None)
                p.set_clip_path(None)
                handle_list.append(p)

            else:
                handle_list.append(None)

            handlebox = DrawingArea(width=self.handlelength * fontsize,
                                    height=height,
                                    xdescent=0.,
                                    ydescent=descent)

            handle = handle_list[-1]
            handlebox.add_artist(handle)
            if hasattr(handle, "_legmarker"):
                handlebox.add_artist(handle._legmarker)
            handleboxes.append(handlebox)

        # We calculate number of lows in each column. The first
        # (num_largecol) columns will have (nrows+1) rows, and remaing
        # (num_smallcol) columns will have (nrows) rows.
        nrows, num_largecol = divmod(len(handleboxes), self._ncol)
        num_smallcol = self._ncol - num_largecol

        # starting index of each column and number of rows in it.
        largecol = safezip(range(0, num_largecol * (nrows + 1), (nrows + 1)),
                           [nrows + 1] * num_largecol)
        smallcol = safezip(
            range(num_largecol * (nrows + 1), len(handleboxes), nrows),
            [nrows] * num_smallcol)

        handle_label = safezip(handleboxes, labelboxes)
        columnbox = []
        for i0, di in largecol + smallcol:
            # pack handleBox and labelBox into itemBox
            itemBoxes = [
                HPacker(pad=0,
                        sep=self.handletextpad * fontsize,
                        children=[h, t],
                        align="baseline") for h, t in handle_label[i0:i0 + di]
            ]
            # minimumdescent=False for the text of the last row of the column
            itemBoxes[-1].get_children()[1].set_minimumdescent(False)

            # pack columnBox
            columnbox.append(
                VPacker(pad=0,
                        sep=self.labelspacing * fontsize,
                        align="baseline",
                        children=itemBoxes))

        if self._mode == "expand":
            mode = "expand"
        else:
            mode = "fixed"

        sep = self.columnspacing * fontsize

        self._legend_box = HPacker(pad=self.borderpad * fontsize,
                                   sep=sep,
                                   align="baseline",
                                   mode=mode,
                                   children=columnbox)

        self._legend_box.set_figure(self.figure)

        self.texts = text_list
        self.legendHandles = handle_list
def plot_portfolio_result(pot_file_name):
    pot_file_name_main = pot_file_name[:-4]
    cfg_file_name = "%s%s" % (pot_file_name_main, "_config.yaml")
    pot_ratio = 1
    point_value = 1
    if (os.path.exists(cfg_file_name)):
        cfg_stream = open(cfg_file_name, 'r')
        cfg = yaml.load(cfg_stream)
        cfg_stream.close()
        if "Ratio" in cfg.keys():
            pot_ratio = cfg["Ratio"]
        if "PointValue" in cfg.keys():
            point_value = cfg["PointValue"]

    Pot_Pnl_df = pd.read_csv(pot_file_name)
    datenum = Pot_Pnl_df["datetimenum"].apply(lambda d: int(d / 1000000))
    profits = Pot_Pnl_df["pnl"] * pot_ratio
    contract_value = Pot_Pnl_df["price"] * point_value * pot_ratio
    profit_peak = profits.cummax()
    drawdown = profits - profit_peak
    profit_peak_idx = profits[profits == profit_peak].index
    contract_peak = Pot_Pnl_df["price"] * point_value * pot_ratio
    ## Get the initial contract_value for drawdown range.
    for ipk in np.arange(1, len(profit_peak_idx)):
        ipk_st = profit_peak_idx[ipk - 1]
        ipk_ed = profit_peak_idx[ipk]
        contract_peak[ipk_st:ipk_ed] = contract_value[ipk_st]
    ipk_st = profit_peak_idx[-1]
    contract_peak[ipk_st:] = contract_value[ipk_st]
    drawdown_ratio = -drawdown / contract_peak
    max_dd_ratio = drawdown_ratio.max()
    max_dd_ratio_idx = drawdown_ratio[drawdown_ratio == max_dd_ratio].index

    # day
    dailyprofitcum = profits.groupby(datenum).last()
    dailyprofit = dailyprofitcum.diff()
    dailyprofit.iloc[0] = dailyprofitcum.iloc[0]
    dailywinrate = float((dailyprofit > 0).sum()) / len(datenum.unique())
    dailysharpe = dailyprofit.mean() / dailyprofit.std() * (242 ** 0.5)
    # month
    mon_idx = (datenum / 100).astype(int)
    mon_idx_diff = mon_idx.diff()
    mon_idx_diff.loc[0] = 1
    mon_idx_diff.loc[Pot_Pnl_df.index[-1]] = 1
    x_mon_idx = mon_idx_diff[mon_idx_diff > 0].index
    ###
    net_profit = profits.tail(1)
    max_drawdown = drawdown.min()  # max drawdown
    sharpe_ratio = dailysharpe
    mdd_idx = drawdown[drawdown == max_drawdown].index
    max_profit = profits.values[0:mdd_idx.max()].max()

    ind1 = 'NP = {}'.format('%.1f' % (net_profit))
    ind2 = 'MDD = {}'.format('%.1f' % (max_drawdown))
    ind3 = 'ShR = {}'.format('%.1f' % (sharpe_ratio))
    ind4 = 'Ratio = {}'.format('%.1f' % (pot_ratio))
    ind5 = 'MDD_R = {}'.format('%.4f' % (max_dd_ratio))

    ind_1 = float(net_profit)
    ind_2 = float(max_drawdown)
    ind_3 = float(sharpe_ratio)
    ind_4 = float(pot_ratio)
    ind_5 = float(max_dd_ratio)

    ##
    fsize = 20
    fig = plt.figure(figsize=(44, 20))
    plt.subplots_adjust(left=0.05, right=0.95, top=0.95, bottom=0.05)
    plt.title(pot_file_name_main, size=(fsize + 5))
    ## contract_value and contract_peak
    plt.plot(Pot_Pnl_df.index, contract_value, color='black')
    # plt.plot(Pot_Pnl_df.index, contract_peak, color = 'blue')
    ## drawdown and profit
    plt.fill_between(Pot_Pnl_df.index, 0, drawdown, facecolor='red', color='red')
    plt.fill_between(Pot_Pnl_df.index, 0, profits, facecolor='green', color='green')

    nowsum = 0
    xT = []
    yT = []
    for x in x_mon_idx.values:
        ptd = profits.values[x]
        y = ptd - nowsum

        if y > 0:
            bbox_props = dict(boxstyle='round', ec='none', fc='#F4F4F4', alpha=0.7)
            plt.text(x, (4 * y), float(format(y, '.2f')), size=fsize, bbox=bbox_props, color='#000000')
        else:
            bbox_props = dict(boxstyle='round', ec='none', fc='#F4F4F4', alpha=0.7)
            plt.text(x, (4 * y), float(format(y, '.2f')), size=fsize, bbox=bbox_props, color='#000000')

        xT.append(x)
        yT.append(str(datenum.values[x]))
        nowsum = ptd

    max_value = contract_value.max()
    for x in max_dd_ratio_idx:
        bbox_props = dict(boxstyle='round', ec='none', fc='r', alpha=0.9)
        plt.text(x, (2.0 * max_value / 3.0), "MDD_R", size=fsize, bbox=bbox_props, color='#000000')

    plt.xticks(xT, yT)
    plt.tick_params(labelsize=fsize)

    bbox_props = dict(boxstyle='round', ec='g', fc='none')
    box1 = TextArea(ind1, textprops=dict(size=fsize, bbox=bbox_props))
    bbox_props = dict(boxstyle='round', ec='r', fc='none')
    box2 = TextArea(ind2, textprops=dict(size=fsize, bbox=bbox_props))
    bbox_props = dict(boxstyle='round', ec='b', fc='none')
    box3 = TextArea(ind3, textprops=dict(size=fsize, bbox=bbox_props))
    bbox_props = dict(boxstyle='round', ec='c', fc='none')
    box4 = TextArea(ind4, textprops=dict(size=fsize, bbox=bbox_props))
    bbox_props = dict(boxstyle='round', ec='c', fc='none')
    box5 = TextArea(ind5, textprops=dict(size=fsize, bbox=bbox_props))

    box = HPacker(children=[box4, box1, box2, box3, box5], pad=0, sep=fsize - 5)
    # box = HPacker(children=[box4, box1, box2, box3], pad=0, sep=fsize-5)

    ax = plt.gca()
    anchored_box = AnchoredOffsetbox(loc=2, child=box, pad=0.2, frameon=False)
    ax.add_artist(anchored_box)

    ax.grid(True)
    ax.autoscale_view()
    fig.autofmt_xdate()

    plt.savefig("%s%s" % (pot_file_name, ".png"))
    plt.close()
    pic_name = "%s%s" % (pot_file_name, ".png")
    return ind_1, ind_2, ind_3, ind_4, ind_5, pic_name
Example #33
0
def plot_levels(var, r0=None, r1=None, nl=10, iso=None, out=False,
                title=True, levels=True):
    global data

    try:
        plt.clf()
    except:
        pass

    if iso is None:
        iso = get_default_iso(data)

    if np.ndim(var.grid.data[0]) == 1:
        X, Y = np.meshgrid(var.grid.data[1], var.grid.data[0])
    else:
        if var.grid.dims == var.dims:
            X = var.grid.data[0]
            Y = var.grid.data[1]
        else:
            X = var.grid_mid.data[0]
            Y = var.grid_mid.data[1]

    if r0 is None:
        r0 = np.min(var.data)
        r1 = np.max(var.data)
        dr = (r1 - r0) / (nl + 1)
        r0 += dr
        r1 -= dr

    rl = r0 + 1.0 * (r1 - r0) * np.array(range(nl)) / (nl - 1)

    fig = plt.gcf()
    if out:
        gs = plt.GridSpec(1, 1)  # , width_ratios=[8, 1])
        ax = plt.subplot(gs[0])
    else:
        ax = plt.gca()

    cs = ax.contour(X, Y, var.data, levels=rl, colors='k', linewidths=0.5)

    if levels:
        fmt = {}
        for l, i in zip(cs.levels, range(1, len(cs.levels)+1)):
            fmt[l] = str(i)

        sidx = ""
        slvl = ""
        for l, i in reversed(list(zip(cs.levels, range(1, len(cs.levels)+1)))):
            # sidx += rtn + "%i" % i
            # slvl += rtn + "%-6.4g" % l
            # rtn = "\n"
            sidx += "%i\n" % i
            slvl += "%-6.4g\n" % l

        t1 = TextArea('Level', textprops=dict(color='k', fontsize='small'))
        t2 = TextArea(sidx, textprops=dict(color='k', fontsize='small'))
        tl = VPacker(children=[t1, t2], align="center", pad=0, sep=2)

        lname = var.name.replace("_node", "")
        t3 = TextArea(lname, textprops=dict(color='k', fontsize='small'))
        t4 = TextArea(slvl, textprops=dict(color='k', fontsize='small'))
        tr = VPacker(children=[t3, t4], align="center", pad=0, sep=2)

        t = HPacker(children=[tl, tr], align="center", pad=0, sep=8)

        if out:
            t = AnchoredOffsetbox(loc=2, child=t, pad=.4,
                                  bbox_to_anchor=(1.01, 1), frameon=True,
                                  bbox_transform=ax.transAxes, borderpad=0)
        else:
            t = AnchoredOffsetbox(loc=1, child=t, pad=.4,
                                  bbox_to_anchor=(1, 1), frameon=True,
                                  bbox_transform=ax.transAxes, borderpad=.4)
        t.set_clip_on(False)
        ax.add_artist(t)

        plt.clabel(cs, cs.levels, fmt=fmt, inline_spacing=2, fontsize=8)

    ax.set_xlabel(var.grid.labels[0] + ' $(' + var.grid.units[0] + ')$')
    ax.set_ylabel(var.grid.labels[1] + ' $(' + var.grid.units[1] + ')$')

    if title:
        if out:
            # suptitle(get_title(), fontsize='large')
            plt.suptitle(get_title(), fontsize='large', y=0.92)
        else:
            plt.title(get_title(), fontsize='large', y=1.03)

    plt.axis('tight')
    if iso:
        plt.axis('image')

    plt.draw()
    if out:
        gs.tight_layout(fig, rect=[0, -0.01, 0.95, 0.92])
        # fw = fig.get_window_extent().width
        # tw = fw*.06+t1.get_children()[0].get_window_extent().width \
        #     + t3.get_children()[0].get_window_extent().width
        # print(1-tw/fw)
        # gs.update(right=1-tw/fw)
        # ax.set_position([box.x0, box.y0, box.width + bw, box.height])
    else:
        fig.set_tight_layout(True)
    plt.draw()