def update_positions(self, renderer): x, y = self.xytext self._x, self._y = self._get_xy(x, y, self.textcoords) x, y = self.xy x, y = self._get_xy(x, y, self.xycoords) if self.arrowprops: x0, y0 = x, y l, b, w, h = self.get_window_extent(renderer).get_bounds() dpi = self.figure.dpi.get() r = l + w t = b + h xc = 0.5 * (l + r) yc = 0.5 * (b + t) # pick the x,y corner of the text bbox closest to point # annotated dsu = [(abs(val - x0), val) for val in l, r, xc] dsu.sort() d, x = dsu[0] dsu = [(abs(val - y0), val) for val in b, t, yc] dsu.sort() d, y = dsu[0] d = self.arrowprops.copy() width = d.pop('width', 4) headwidth = d.pop('headwidth', 12) frac = d.pop('frac', 0.1) shrink = d.pop('shrink', 0.0) theta = math.atan2(y - y0, x - x0) r = math.sqrt((y - y0)**2. + (x - x0)**2.) dx = shrink * r * math.cos(theta) dy = shrink * r * math.sin(theta) self.arrow = YAArrow(self.figure.dpi, (x0 + dx, y0 + dy), (x - dx, y - dy), width=width, headwidth=headwidth, frac=frac, **d) self.arrow.set_clip_box(self.get_clip_box())
def update_positions(self, renderer): x, y = self.xytext self._x, self._y = self._get_xy(x, y, self.textcoords) x, y = self.xy x, y = self._get_xy(x, y, self.xycoords) if self.arrowprops: x0, y0 = x, y l,b,w,h = self.get_window_extent(renderer).bounds dpi = self.figure.dpi r = l+w t = b+h xc = 0.5*(l+r) yc = 0.5*(b+t) # pick the x,y corner of the text bbox closest to point # annotated dsu = [(abs(val-x0), val) for val in l, r, xc] dsu.sort() d, x = dsu[0] dsu = [(abs(val-y0), val) for val in b, t, yc] dsu.sort() d, y = dsu[0] d = self.arrowprops.copy() width = d.pop('width', 4) headwidth = d.pop('headwidth', 12) frac = d.pop('frac', 0.1) shrink = d.pop('shrink', 0.0) theta = math.atan2(y-y0, x-x0) r = math.sqrt((y-y0)**2. + (x-x0)**2.) dx = shrink*r*math.cos(theta) dy = shrink*r*math.sin(theta) self.arrow = YAArrow(self.figure.dpi, (x0+dx,y0+dy), (x-dx, y-dy), width=width, headwidth=headwidth, frac=frac, **d) self.arrow.set_clip_box(self.get_clip_box())
class Annotation(Text): """ A Text class to make annotating things in the figure: Figure, Axes, Point, Rectangle, etc... easier """ def __str__(self): return "Annotation(%g,%g,%s)"%(self.xy[0],self.xy[1],self._text) def __init__(self, s, xy, xytext=None, xycoords='data', textcoords=None, arrowprops=None, **kwargs): """ Annotate the x,y point xy with text s at x,y location xytext (xytext if None defaults to xy and textcoords if None defaults to xycoords). arrowprops, if not None, is a dictionary of line properties (see matplotlib.lines.Line2D) for the arrow that connects annotation to the point. Valid keys are - width : the width of the arrow in points - frac : the fraction of the arrow length occupied by the head - headwidth : the width of the base of the arrow head in points - shrink: often times it is convenient to have the arrowtip and base a bit away from the text and point being annotated. If d is the distance between the text and annotated point, shrink will shorten the arrow so the tip and base are shink percent of the distance d away from the endpoints. ie, shrink=0.05 is 5%% - any key for matplotlib.patches.polygon xycoords and textcoords are strings that indicate the coordinates of xy and xytext. 'figure points' : points from the lower left corner of the figure 'figure pixels' : pixels from the lower left corner of the figure 'figure fraction' : 0,0 is lower left of figure and 1,1 is upper, right 'axes points' : points from lower left corner of axes 'axes pixels' : pixels from lower left corner of axes 'axes fraction' : 0,1 is lower left of axes and 1,1 is upper right 'data' : use the coordinate system of the object being annotated (default) 'offset points' : Specify an offset (in points) from the xy value 'polar' : you can specify theta, r for the annotation, even in cartesian plots. Note that if you are using a polar axes, you do not need to specify polar for the coordinate system since that is the native"data" coordinate system. If a points or pixels option is specified, values will be added to the left, bottom and if negative, values will be subtracted from the top, right. Eg, # 10 points to the right of the left border of the axes and # 5 points below the top border xy=(10,-5), xycoords='axes points' Additional kwargs are Text properties: %(Text)s """ if xytext is None: xytext = xy if textcoords is None: textcoords = xycoords # we'll draw ourself after the artist we annotate by default x,y = self.xytext = xytext Text.__init__(self, x, y, s, **kwargs) self.xy = xy self.arrowprops = arrowprops self.arrow = None self.xycoords = xycoords self.textcoords = textcoords __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def contains(self,event): t,tinfo = Text.contains(self,event) if self.arrow is not None: a,ainfo=self.arrow.contains(event) t = t or a return t,tinfo def set_clip_box(self, clipbox): """ Set the artist's clip Bbox ACCEPTS: a matplotlib.transform.Bbox instance """ Text.set_clip_box(self, clipbox) def _get_xy(self, x, y, s): if s=='data': trans = self.axes.transData x = float(self.convert_xunits(x)) y = float(self.convert_yunits(y)) return trans.transform_point((x, y)) elif s=='offset points': # convert the data point dx, dy = self.xy # prevent recursion if self.xycoords == 'offset points': return self._get_xy(dx, dy, 'data') dx, dy = self._get_xy(dx, dy, self.xycoords) # convert the offset dpi = self.figure.dpi.get() x *= dpi/72. y *= dpi/72. # add the offset to the data point x += dx y += dy return x, y elif s=='polar': theta, r = x, y x = r*npy.cos(theta) y = r*npy.sin(theta) trans = self.axes.transData return trans.transform_point((x,y)) elif s=='figure points': #points from the lower left corner of the figure dpi = self.figure.dpi l,b,w,h = self.figure.bbox.bounds r = l+w t = b+h x *= dpi/72. y *= dpi/72. if x<0: x = r + x if y<0: y = t + y return x,y elif s=='figure pixels': #pixels from the lower left corner of the figure l,b,w,h = self.figure.bbox.bounds r = l+w t = b+h if x<0: x = r + x if y<0: y = t + y return x, y elif s=='figure fraction': #(0,0) is lower left, (1,1) is upper right of figure trans = self.figure.transFigure return trans.transform_point((x,y)) elif s=='axes points': #points from the lower left corner of the axes dpi = self.figure.dpi l,b,w,h = self.axes.bbox.bounds r = l+w t = b+h if x<0: x = r + x*dpi/72. else: x = l + x*dpi/72. if y<0: y = t + y*dpi/72. else: y = b + y*dpi/72. return x, y elif s=='axes pixels': #pixels from the lower left corner of the axes l,b,w,h = self.axes.bbox.bounds r = l+w t = b+h if x<0: x = r + x else: x = l + x if y<0: y = t + y else: y = b + y return x, y elif s=='axes fraction': #(0,0) is lower left, (1,1) is upper right of axes trans = self.axes.transAxes return trans.transform_point((x, y)) def update_positions(self, renderer): x, y = self.xytext self._x, self._y = self._get_xy(x, y, self.textcoords) x, y = self.xy x, y = self._get_xy(x, y, self.xycoords) if self.arrowprops: x0, y0 = x, y l,b,w,h = self.get_window_extent(renderer).bounds dpi = self.figure.dpi r = l+w t = b+h xc = 0.5*(l+r) yc = 0.5*(b+t) # pick the x,y corner of the text bbox closest to point # annotated dsu = [(abs(val-x0), val) for val in l, r, xc] dsu.sort() d, x = dsu[0] dsu = [(abs(val-y0), val) for val in b, t, yc] dsu.sort() d, y = dsu[0] d = self.arrowprops.copy() width = d.pop('width', 4) headwidth = d.pop('headwidth', 12) frac = d.pop('frac', 0.1) shrink = d.pop('shrink', 0.0) theta = math.atan2(y-y0, x-x0) r = math.sqrt((y-y0)**2. + (x-x0)**2.) dx = shrink*r*math.cos(theta) dy = shrink*r*math.sin(theta) self.arrow = YAArrow(self.figure.dpi, (x0+dx,y0+dy), (x-dx, y-dy), width=width, headwidth=headwidth, frac=frac, **d) self.arrow.set_clip_box(self.get_clip_box()) def draw(self, renderer): self.update_positions(renderer) if self.arrow is not None: self.arrow.draw(renderer) Text.draw(self, renderer)
class Annotation(Text): """ A Text class to make annotating things in the figure: Figure, Axes, Point, Rectangle, etc... easier """ def __str__(self): return "Annotation(%g,%g,%s)" % (self.xy[0], self.xy[1], self._text) def __init__(self, s, xy, xytext=None, xycoords='data', textcoords=None, arrowprops=None, **kwargs): """ Annotate the x,y point xy with text s at x,y location xytext (xytext if None defaults to xy and textcoords if None defaults to xycoords). arrowprops, if not None, is a dictionary of line properties (see matplotlib.lines.Line2D) for the arrow that connects annotation to the point. Valid keys are - width : the width of the arrow in points - frac : the fraction of the arrow length occupied by the head - headwidth : the width of the base of the arrow head in points - shrink: often times it is convenient to have the arrowtip and base a bit away from the text and point being annotated. If d is the distance between the text and annotated point, shrink will shorten the arrow so the tip and base are shink percent of the distance d away from the endpoints. ie, shrink=0.05 is 5%% - any key for matplotlib.patches.polygon xycoords and textcoords are strings that indicate the coordinates of xy and xytext. 'figure points' : points from the lower left corner of the figure 'figure pixels' : pixels from the lower left corner of the figure 'figure fraction' : 0,0 is lower left of figure and 1,1 is upper, right 'axes points' : points from lower left corner of axes 'axes pixels' : pixels from lower left corner of axes 'axes fraction' : 0,1 is lower left of axes and 1,1 is upper right 'data' : use the coordinate system of the object being annotated (default) 'offset points' : Specify an offset (in points) from the xy value 'polar' : you can specify theta, r for the annotation, even in cartesian plots. Note that if you are using a polar axes, you do not need to specify polar for the coordinate system since that is the native"data" coordinate system. If a points or pixels option is specified, values will be added to the left, bottom and if negative, values will be subtracted from the top, right. Eg, # 10 points to the right of the left border of the axes and # 5 points below the top border xy=(10,-5), xycoords='axes points' Additional kwargs are Text properties: %(Text)s """ if xytext is None: xytext = xy if textcoords is None: textcoords = xycoords # we'll draw ourself after the artist we annotate by default x, y = self.xytext = xytext Text.__init__(self, x, y, s, **kwargs) self.xy = xy self.arrowprops = arrowprops self.arrow = None self.xycoords = xycoords self.textcoords = textcoords __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def contains(self, event): t, tinfo = Text.contains(self, event) if self.arrow is not None: a, ainfo = self.arrow.contains(event) t = t or a return t, tinfo def set_clip_box(self, clipbox): """ Set the artist's clip Bbox ACCEPTS: a matplotlib.transform.Bbox instance """ Text.set_clip_box(self, clipbox) def _get_xy(self, x, y, s): if s == 'data': trans = self.axes.transData x = float(self.convert_xunits(x)) y = float(self.convert_yunits(y)) return trans.xy_tup((x, y)) elif s == 'offset points': # convert the data point dx, dy = self.xy # prevent recursion if self.xycoords == 'offset points': return self._get_xy(dx, dy, 'data') dx, dy = self._get_xy(dx, dy, self.xycoords) # convert the offset dpi = self.figure.dpi.get() x *= dpi / 72. y *= dpi / 72. # add the offset to the data point x += dx y += dy return x, y elif s == 'polar': theta, r = x, y x = r * npy.cos(theta) y = r * npy.sin(theta) trans = self.axes.transData return trans.xy_tup((x, y)) elif s == 'figure points': #points from the lower left corner of the figure dpi = self.figure.dpi.get() l, b, w, h = self.figure.bbox.get_bounds() r = l + w t = b + h x *= dpi / 72. y *= dpi / 72. if x < 0: x = r + x if y < 0: y = t + y return x, y elif s == 'figure pixels': #pixels from the lower left corner of the figure l, b, w, h = self.figure.bbox.get_bounds() r = l + w t = b + h if x < 0: x = r + x if y < 0: y = t + y return x, y elif s == 'figure fraction': #(0,0) is lower left, (1,1) is upper right of figure trans = self.figure.transFigure return trans.xy_tup((x, y)) elif s == 'axes points': #points from the lower left corner of the axes dpi = self.figure.dpi.get() l, b, w, h = self.axes.bbox.get_bounds() r = l + w t = b + h if x < 0: x = r + x * dpi / 72. else: x = l + x * dpi / 72. if y < 0: y = t + y * dpi / 72. else: y = b + y * dpi / 72. return x, y elif s == 'axes pixels': #pixels from the lower left corner of the axes l, b, w, h = self.axes.bbox.get_bounds() r = l + w t = b + h if x < 0: x = r + x else: x = l + x if y < 0: y = t + y else: y = b + y return x, y elif s == 'axes fraction': #(0,0) is lower left, (1,1) is upper right of axes trans = self.axes.transAxes return trans.xy_tup((x, y)) def update_positions(self, renderer): x, y = self.xytext self._x, self._y = self._get_xy(x, y, self.textcoords) x, y = self.xy x, y = self._get_xy(x, y, self.xycoords) if self.arrowprops: x0, y0 = x, y l, b, w, h = self.get_window_extent(renderer).get_bounds() dpi = self.figure.dpi.get() r = l + w t = b + h xc = 0.5 * (l + r) yc = 0.5 * (b + t) # pick the x,y corner of the text bbox closest to point # annotated dsu = [(abs(val - x0), val) for val in l, r, xc] dsu.sort() d, x = dsu[0] dsu = [(abs(val - y0), val) for val in b, t, yc] dsu.sort() d, y = dsu[0] d = self.arrowprops.copy() width = d.pop('width', 4) headwidth = d.pop('headwidth', 12) frac = d.pop('frac', 0.1) shrink = d.pop('shrink', 0.0) theta = math.atan2(y - y0, x - x0) r = math.sqrt((y - y0)**2. + (x - x0)**2.) dx = shrink * r * math.cos(theta) dy = shrink * r * math.sin(theta) self.arrow = YAArrow(self.figure.dpi, (x0 + dx, y0 + dy), (x - dx, y - dy), width=width, headwidth=headwidth, frac=frac, **d) self.arrow.set_clip_box(self.get_clip_box()) def draw(self, renderer): self.update_positions(renderer) if self.arrow is not None: self.arrow.draw(renderer) Text.draw(self, renderer)
class Annotation(Text): """ A :class:`~matplotlib.text.Text` class to make annotating things in the figure, such as :class:`~matplotlib.figure.Figure`, :class:`~matplotlib.axes.Axes`, :class:`~matplotlib.patches.Rectangle`, etc., easier. """ def __str__(self): return "Annotation(%g,%g,%s)" % (self.xy[0], self.xy[1], repr(self._text)) def __init__(self, s, xy, xytext=None, xycoords="data", textcoords=None, arrowprops=None, **kwargs): """ Annotate the *x*, *y* point *xy* with text *s* at *x*, *y* location *xytext*. (If *xytext* = *None*, defaults to *xy*, and if *textcoords* = *None*, defaults to *xycoords*). *arrowprops*, if not *None*, is a dictionary of line properties (see :class:`matplotlib.lines.Line2D`) for the arrow that connects annotation to the point. Valid keys are ========= =========================================================== Key Description ========= =========================================================== width the width of the arrow in points frac the fraction of the arrow length occupied by the head headwidth the width of the base of the arrow head in points shrink oftentimes it is convenient to have the arrowtip and base a bit away from the text and point being annotated. If *d* is the distance between the text and annotated point, shrink will shorten the arrow so the tip and base are shink percent of the distance *d* away from the endpoints. ie, ``shrink=0.05 is 5%%`` ? any key for :class:`matplotlib.patches.polygon` ========= =========================================================== *xycoords* and *textcoords* are strings that indicate the coordinates of *xy* and *xytext*. ================= =================================================== Property Description ================= =================================================== 'figure points' points from the lower left corner of the figure 'figure pixels' pixels from the lower left corner of the figure 'figure fraction' 0,0 is lower left of figure and 1,1 is upper, right 'axes points' points from lower left corner of axes 'axes pixels' pixels from lower left corner of axes 'axes fraction' 0,1 is lower left of axes and 1,1 is upper right 'data' use the coordinate system of the object being annotated (default) 'offset points' Specify an offset (in points) from the *xy* value 'polar' you can specify *theta*, *r* for the annotation, even in cartesian plots. Note that if you are using a polar axes, you do not need to specify polar for the coordinate system since that is the native "data" coordinate system. ================= =================================================== If a 'points' or 'pixels' option is specified, values will be added to the bottom-left and if negative, values will be subtracted from the top-right. Eg:: # 10 points to the right of the left border of the axes and # 5 points below the top border xy=(10,-5), xycoords='axes points' Additional kwargs are Text properties: %(Text)s """ if xytext is None: xytext = xy if textcoords is None: textcoords = xycoords # we'll draw ourself after the artist we annotate by default x, y = self.xytext = xytext Text.__init__(self, x, y, s, **kwargs) self.xy = xy self.arrowprops = arrowprops self.arrow = None self.xycoords = xycoords self.textcoords = textcoords __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def contains(self, event): t, tinfo = Text.contains(self, event) if self.arrow is not None: a, ainfo = self.arrow.contains(event) t = t or a return t, tinfo def set_figure(self, fig): if self.arrow is not None: self.arrow.set_figure(fig) Artist.set_figure(self, fig) def set_clip_box(self, clipbox): """ Set the artist's clip Bbox ACCEPTS: a matplotlib.transform.Bbox instance """ Text.set_clip_box(self, clipbox) def _get_xy(self, x, y, s): if s == "data": trans = self.axes.transData x = float(self.convert_xunits(x)) y = float(self.convert_yunits(y)) return trans.transform_point((x, y)) elif s == "offset points": # convert the data point dx, dy = self.xy # prevent recursion if self.xycoords == "offset points": return self._get_xy(dx, dy, "data") dx, dy = self._get_xy(dx, dy, self.xycoords) # convert the offset dpi = self.figure.get_dpi() x *= dpi / 72.0 y *= dpi / 72.0 # add the offset to the data point x += dx y += dy return x, y elif s == "polar": theta, r = x, y x = r * np.cos(theta) y = r * np.sin(theta) trans = self.axes.transData return trans.transform_point((x, y)) elif s == "figure points": # points from the lower left corner of the figure dpi = self.figure.dpi l, b, w, h = self.figure.bbox.bounds r = l + w t = b + h x *= dpi / 72.0 y *= dpi / 72.0 if x < 0: x = r + x if y < 0: y = t + y return x, y elif s == "figure pixels": # pixels from the lower left corner of the figure l, b, w, h = self.figure.bbox.bounds r = l + w t = b + h if x < 0: x = r + x if y < 0: y = t + y return x, y elif s == "figure fraction": # (0,0) is lower left, (1,1) is upper right of figure trans = self.figure.transFigure return trans.transform_point((x, y)) elif s == "axes points": # points from the lower left corner of the axes dpi = self.figure.dpi l, b, w, h = self.axes.bbox.bounds r = l + w t = b + h if x < 0: x = r + x * dpi / 72.0 else: x = l + x * dpi / 72.0 if y < 0: y = t + y * dpi / 72.0 else: y = b + y * dpi / 72.0 return x, y elif s == "axes pixels": # pixels from the lower left corner of the axes l, b, w, h = self.axes.bbox.bounds r = l + w t = b + h if x < 0: x = r + x else: x = l + x if y < 0: y = t + y else: y = b + y return x, y elif s == "axes fraction": # (0,0) is lower left, (1,1) is upper right of axes trans = self.axes.transAxes return trans.transform_point((x, y)) def update_positions(self, renderer): x, y = self.xytext self._x, self._y = self._get_xy(x, y, self.textcoords) x, y = self.xy x, y = self._get_xy(x, y, self.xycoords) if self.arrowprops: x0, y0 = x, y l, b, w, h = self.get_window_extent(renderer).bounds r = l + w t = b + h xc = 0.5 * (l + r) yc = 0.5 * (b + t) # pick the x,y corner of the text bbox closest to point # annotated dsu = [(abs(val - x0), val) for val in l, r, xc] dsu.sort() d, x = dsu[0] dsu = [(abs(val - y0), val) for val in b, t, yc] dsu.sort() d, y = dsu[0] d = self.arrowprops.copy() width = d.pop("width", 4) headwidth = d.pop("headwidth", 12) frac = d.pop("frac", 0.1) shrink = d.pop("shrink", 0.0) theta = math.atan2(y - y0, x - x0) r = math.sqrt((y - y0) ** 2.0 + (x - x0) ** 2.0) dx = shrink * r * math.cos(theta) dy = shrink * r * math.sin(theta) self.arrow = YAArrow( self.figure, (x0 + dx, y0 + dy), (x - dx, y - dy), width=width, headwidth=headwidth, frac=frac, **d ) self.arrow.set_clip_box(self.get_clip_box()) def draw(self, renderer): self.update_positions(renderer) if self.arrow is not None: if self.arrow.figure is None and self.figure is not None: self.arrow.figure = self.figure self.arrow.draw(renderer) Text.draw(self, renderer)