source: branches/polybatch/python/asaplotbase.py@ 2877

Last change on this file since 2877 was 1870, checked in by Malte Marquarding, 14 years ago

Added svg support

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.2 KB
Line 
1"""
2ASAP plotting class based on matplotlib.
3"""
4
5import sys
6from re import match
7import matplotlib
8
9from matplotlib.figure import Figure, Text
10from matplotlib.font_manager import FontProperties as FP
11from numpy import sqrt
12from matplotlib import rc, rcParams
13from matplotlib.ticker import OldScalarFormatter
14
15from asap.parameters import rcParams as asaprcParams
16from asap.logging import asaplog
17
18# API change in mpl >= 0.98
19try:
20 from matplotlib.transforms import blended_transform_factory
21except ImportError:
22 from matplotlib.transforms import blend_xy_sep_transform as blended_transform_factory
23
24if int(matplotlib.__version__.split(".")[1]) < 99:
25 #print "Warning: matplotlib version < 0.87. This might cause errors. Please upgrade."
26 asaplog.push( "matplotlib version < 0.99. This might cause errors. Please upgrade." )
27 asaplog.post( 'WARN' )
28
29class asaplotbase:
30 """
31 ASAP plotting base class based on matplotlib.
32 """
33
34 def __init__(self, rows=1, cols=0, title='', size=None, buffering=False):
35 """
36 Create a new instance of the ASAPlot plotting class.
37
38 If rows < 1 then a separate call to set_panels() is required to define
39 the panel layout; refer to the doctext for set_panels().
40 """
41 self.is_dead = False
42 self.figure = Figure(figsize=size, facecolor='#ddddee')
43 self.canvas = None
44
45 self.set_title(title)
46 self.subplots = []
47 if rows > 0:
48 self.set_panels(rows, cols)
49
50 # Set matplotlib default colour sequence.
51 self.colormap = "green red black cyan magenta orange blue purple yellow pink".split()
52
53 c = asaprcParams['plotter.colours']
54 if isinstance(c,str) and len(c) > 0:
55 self.colormap = c.split()
56
57 self.lsalias = {"line": [1,0],
58 "dashdot": [4,2,1,2],
59 "dashed" : [4,2,4,2],
60 "dotted" : [1,2],
61 "dashdotdot": [4,2,1,2,1,2],
62 "dashdashdot": [4,2,4,2,1,2]
63 }
64
65 styles = "line dashed dotted dashdot".split()
66 c = asaprcParams['plotter.linestyles']
67 if isinstance(c,str) and len(c) > 0:
68 styles = c.split()
69 s = []
70 for ls in styles:
71 if self.lsalias.has_key(ls):
72 s.append(self.lsalias.get(ls))
73 else:
74 s.append('-')
75 self.linestyles = s
76
77 self.color = 0;
78 self.linestyle = 0;
79 self.attributes = {}
80 self.loc = 0
81
82 self.buffering = buffering
83
84 def clear(self):
85 """
86 Delete all lines from the plot. Line numbering will restart from 0.
87 """
88
89 for i in range(len(self.lines)):
90 self.delete(i)
91 self.axes.clear()
92 self.color = 0
93 self.lines = []
94
95 def palette(self, color, colormap=None, linestyle=0, linestyles=None):
96 if colormap:
97 if isinstance(colormap,list):
98 self.colormap = colormap
99 elif isinstance(colormap,str):
100 self.colormap = colormap.split()
101 if 0 <= color < len(self.colormap):
102 self.color = color
103 if linestyles:
104 self.linestyles = []
105 if isinstance(linestyles,list):
106 styles = linestyles
107 elif isinstance(linestyles,str):
108 styles = linestyles.split()
109 for ls in styles:
110 if self.lsalias.has_key(ls):
111 self.linestyles.append(self.lsalias.get(ls))
112 else:
113 self.linestyles.append(self.lsalias.get('line'))
114 if 0 <= linestyle < len(self.linestyles):
115 self.linestyle = linestyle
116
117 def delete(self, numbers=None):
118 """
119 Delete the 0-relative line number, default is to delete the last.
120 The remaining lines are NOT renumbered.
121 """
122
123 if numbers is None: numbers = [len(self.lines)-1]
124
125 if not hasattr(numbers, '__iter__'):
126 numbers = [numbers]
127
128 for number in numbers:
129 if 0 <= number < len(self.lines):
130 if self.lines[number] is not None:
131 for line in self.lines[number]:
132 line.set_linestyle('None')
133 self.lines[number] = None
134 self.show()
135
136 def get_line(self):
137 """
138 Get the current default line attributes.
139 """
140 return self.attributes
141
142
143 def hist(self, x=None, y=None, fmt=None, add=None):
144 """
145 Plot a histogram. N.B. the x values refer to the start of the
146 histogram bin.
147
148 fmt is the line style as in plot().
149 """
150 from numpy import array
151 from numpy.ma import MaskedArray
152 if x is None:
153 if y is None: return
154 x = range(len(y))
155
156 if len(x) != len(y):
157 return
158 l2 = 2*len(x)
159 x2 = range(l2)
160 y2 = range(12)
161 y2 = range(l2)
162 m2 = range(l2)
163 ymsk = None
164 ydat = None
165 if hasattr(y, "raw_mask"):
166 # numpy < 1.1
167 ymsk = y.raw_mask()
168 ydat = y.raw_data()
169 else:
170 ymsk = y.mask
171 ydat = y.data
172 for i in range(l2):
173 x2[i] = x[i/2]
174 m2[i] = ymsk[i/2]
175
176 y2[0] = 0.0
177 for i in range(1,l2):
178 y2[i] = ydat[(i-1)/2]
179
180 self.plot(x2, MaskedArray(y2,mask=m2,copy=0), fmt, add)
181
182
183 def hold(self, hold=True):
184 """
185 Buffer graphics until subsequently released.
186 """
187 self.buffering = hold
188
189
190 def legend(self, loc=None):
191 """
192 Add a legend to the plot.
193
194 Any other value for loc else disables the legend:
195 1: upper right
196 2: upper left
197 3: lower left
198 4: lower right
199 5: right
200 6: center left
201 7: center right
202 8: lower center
203 9: upper center
204 10: center
205
206 """
207 if isinstance(loc, int):
208 self.loc = None
209 if 0 <= loc <= 10: self.loc = loc
210 else:
211 self.loc = None
212 #self.show()
213
214
215 def plot(self, x=None, y=None, fmt=None, add=None):
216 """
217 Plot the next line in the current frame using the current line
218 attributes. The ASAPlot graphics window will be mapped and raised.
219
220 The argument list works a bit like the matlab plot() function.
221 """
222 if x is None:
223 if y is None: return
224 x = range(len(y))
225
226 elif y is None:
227 y = x
228 x = range(len(y))
229 if fmt is None:
230 line = self.axes.plot(x, y)
231 else:
232 line = self.axes.plot(x, y, fmt)
233
234 # Add to an existing line?
235 i = None
236 if add is None or len(self.lines) < add < 0:
237 # Don't add.
238 self.lines.append(line)
239 i = len(self.lines) - 1
240 else:
241 if add == 0: add = len(self.lines)
242 i = add - 1
243 self.lines[i].extend(line)
244
245 # Set/reset attributes for the line.
246 gotcolour = False
247 for k, v in self.attributes.iteritems():
248 if k == 'color': gotcolour = True
249 for segment in self.lines[i]:
250 getattr(segment, "set_%s"%k)(v)
251
252 if not gotcolour and len(self.colormap):
253 for segment in self.lines[i]:
254 getattr(segment, "set_color")(self.colormap[self.color])
255 if len(self.colormap) == 1:
256 getattr(segment, "set_dashes")(self.linestyles[self.linestyle])
257
258 self.color += 1
259 if self.color >= len(self.colormap):
260 self.color = 0
261
262 if len(self.colormap) == 1:
263 self.linestyle += 1
264 if self.linestyle >= len(self.linestyles):
265 self.linestyle = 0
266
267 self.show()
268
269
270 def position(self):
271 """
272 Use the mouse to get a position from a graph.
273 """
274
275 def position_disable(event):
276 self.register('button_press', None)
277 print '%.4f, %.4f' % (event.xdata, event.ydata)
278
279 print 'Press any mouse button...'
280 self.register('button_press', position_disable)
281
282
283 def get_region(self):
284 pos = []
285 print "Please select the bottom/left point"
286 pos.append(self.figure.ginput(n=1, show_clicks=False)[0])
287 print "Please select the top/right point"
288 pos.append(self.figure.ginput(n=1, show_clicks=False)[0])
289 return pos
290
291 def get_point(self):
292 print "Please select the point"
293 pt = self.figure.ginput(n=1, show_clicks=False)
294 if pt:
295 return pt[0]
296 else:
297 return None
298
299 def region(self):
300 """
301 Use the mouse to get a rectangular region from a plot.
302
303 The return value is [x0, y0, x1, y1] in world coordinates.
304 """
305
306 def region_start(event):
307 self.rect = {'x': event.x, 'y': event.y,
308 'world': [event.xdata, event.ydata,
309 event.xdata, event.ydata]}
310 self.register('button_press', None)
311 self.register('motion_notify', region_draw)
312 self.register('button_release', region_disable)
313
314 def region_draw(event):
315 self.figmgr.toolbar.draw_rubberband(event, event.x, event.y,
316 self.rect['x'], self.rect['y'])
317
318 def region_disable(event):
319 self.register('motion_notify', None)
320 self.register('button_release', None)
321
322 self.rect['world'][2:4] = [event.xdata, event.ydata]
323 print '(%.2f, %.2f) (%.2f, %.2f)' % (self.rect['world'][0],
324 self.rect['world'][1], self.rect['world'][2],
325 self.rect['world'][3])
326 self.figmgr.toolbar.release(event)
327
328 self.register('button_press', region_start)
329
330 # This has to be modified to block and return the result (currently
331 # printed by region_disable) when that becomes possible in matplotlib.
332
333 return [0.0, 0.0, 0.0, 0.0]
334
335
336 def register(self, type=None, func=None):
337 """
338 Register, reregister, or deregister events of type 'button_press',
339 'button_release', or 'motion_notify'.
340
341 The specified callback function should have the following signature:
342
343 def func(event)
344
345 where event is an MplEvent instance containing the following data:
346
347 name # Event name.
348 canvas # FigureCanvas instance generating the event.
349 x = None # x position - pixels from left of canvas.
350 y = None # y position - pixels from bottom of canvas.
351 button = None # Button pressed: None, 1, 2, 3.
352 key = None # Key pressed: None, chr(range(255)), shift,
353 win, or control
354 inaxes = None # Axes instance if cursor within axes.
355 xdata = None # x world coordinate.
356 ydata = None # y world coordinate.
357
358 For example:
359
360 def mouse_move(event):
361 print event.xdata, event.ydata
362
363 a = asaplot()
364 a.register('motion_notify', mouse_move)
365
366 If func is None, the event is deregistered.
367
368 Note that in TkAgg keyboard button presses don't generate an event.
369 """
370
371 if not self.events.has_key(type): return
372
373 if func is None:
374 if self.events[type] is not None:
375 # It's not clear that this does anything.
376 self.canvas.mpl_disconnect(self.events[type])
377 self.events[type] = None
378
379 # It seems to be necessary to return events to the toolbar. <-- Not ture. 2010.Jul.14.kana.
380 #if type == 'motion_notify':
381 # self.canvas.mpl_connect(type + '_event',
382 # self.figmgr.toolbar.mouse_move)
383 #elif type == 'button_press':
384 # self.canvas.mpl_connect(type + '_event',
385 # self.figmgr.toolbar.press)
386 #elif type == 'button_release':
387 # self.canvas.mpl_connect(type + '_event',
388 # self.figmgr.toolbar.release)
389
390 else:
391 self.events[type] = self.canvas.mpl_connect(type + '_event', func)
392
393
394 def release(self):
395 """
396 Release buffered graphics.
397 """
398 self.buffering = False
399 self.show()
400
401
402 def save(self, fname=None, orientation=None, dpi=None, papertype=None):
403 """
404 Save the plot to a file.
405
406 fname is the name of the output file. The image format is determined
407 from the file suffix; 'png', 'ps', and 'eps' are recognized. If no
408 file name is specified 'yyyymmdd_hhmmss.png' is created in the current
409 directory.
410 """
411 from asap import rcParams
412 if papertype is None:
413 papertype = rcParams['plotter.papertype']
414 if fname is None:
415 from datetime import datetime
416 dstr = datetime.now().strftime('%Y%m%d_%H%M%S')
417 fname = 'asap'+dstr+'.png'
418
419 d = ['png','.ps','eps', 'svg']
420
421 from os.path import expandvars
422 fname = expandvars(fname)
423
424 if fname[-3:].lower() in d:
425 try:
426 if fname[-3:].lower() == ".ps":
427 from matplotlib import __version__ as mv
428 w = self.figure.get_figwidth()
429 h = self.figure.get_figheight()
430
431 if orientation is None:
432 # oriented
433 if w > h:
434 orientation = 'landscape'
435 else:
436 orientation = 'portrait'
437 from matplotlib.backends.backend_ps import papersize
438 pw,ph = papersize[papertype.lower()]
439 ds = None
440 if orientation == 'landscape':
441 ds = min(ph/w, pw/h)
442 else:
443 ds = min(pw/w, ph/h)
444 ow = ds * w
445 oh = ds * h
446 self.figure.set_size_inches((ow, oh))
447 self.figure.savefig(fname, orientation=orientation,
448 papertype=papertype.lower())
449 self.figure.set_size_inches((w, h))
450 print 'Written file %s' % (fname)
451 else:
452 if dpi is None:
453 dpi =150
454 self.figure.savefig(fname,dpi=dpi)
455 print 'Written file %s' % (fname)
456 except IOError, msg:
457 #print 'Failed to save %s: Error msg was\n\n%s' % (fname, err)
458 asaplog.post()
459 asaplog.push('Failed to save %s: Error msg was\n\n%s' % (fname, str(msg)))
460 asaplog.post( 'ERROR' )
461 return
462 else:
463 #print "Invalid image type. Valid types are:"
464 #print "'ps', 'eps', 'png'"
465 asaplog.push( "Invalid image type. Valid types are:" )
466 asaplog.push( "'ps', 'eps', 'png', 'svg'" )
467 asaplog.post('WARN')
468
469
470 def set_axes(self, what=None, *args, **kwargs):
471 """
472 Set attributes for the axes by calling the relevant Axes.set_*()
473 method. Colour translation is done as described in the doctext
474 for palette().
475 """
476
477 if what is None: return
478 if what[-6:] == 'colour': what = what[:-6] + 'color'
479
480 key = "colour"
481 if kwargs.has_key(key):
482 val = kwargs.pop(key)
483 kwargs["color"] = val
484
485 getattr(self.axes, "set_%s"%what)(*args, **kwargs)
486
487 self.show(hardrefresh=False)
488
489
490 def set_figure(self, what=None, *args, **kwargs):
491 """
492 Set attributes for the figure by calling the relevant Figure.set_*()
493 method. Colour translation is done as described in the doctext
494 for palette().
495 """
496
497 if what is None: return
498 if what[-6:] == 'colour': what = what[:-6] + 'color'
499 #if what[-5:] == 'color' and len(args):
500 # args = (get_colour(args[0]),)
501
502 newargs = {}
503 for k, v in kwargs.iteritems():
504 k = k.lower()
505 if k == 'colour': k = 'color'
506 newargs[k] = v
507
508 getattr(self.figure, "set_%s"%what)(*args, **newargs)
509 self.show(hardrefresh=False)
510
511
512 def set_limits(self, xlim=None, ylim=None):
513 """
514 Set x-, and y-limits for each subplot.
515
516 xlim = [xmin, xmax] as in axes.set_xlim().
517 ylim = [ymin, ymax] as in axes.set_ylim().
518 """
519 for s in self.subplots:
520 self.axes = s['axes']
521 self.lines = s['lines']
522 oldxlim = list(self.axes.get_xlim())
523 oldylim = list(self.axes.get_ylim())
524 if xlim is not None:
525 for i in range(len(xlim)):
526 if xlim[i] is not None:
527 oldxlim[i] = xlim[i]
528 if ylim is not None:
529 for i in range(len(ylim)):
530 if ylim[i] is not None:
531 oldylim[i] = ylim[i]
532 self.axes.set_xlim(oldxlim)
533 self.axes.set_ylim(oldylim)
534 return
535
536
537 def set_line(self, number=None, **kwargs):
538 """
539 Set attributes for the specified line, or else the next line(s)
540 to be plotted.
541
542 number is the 0-relative number of a line that has already been
543 plotted. If no such line exists, attributes are recorded and used
544 for the next line(s) to be plotted.
545
546 Keyword arguments specify Line2D attributes, e.g. color='r'. Do
547
548 import matplotlib
549 help(matplotlib.lines)
550
551 The set_* methods of class Line2D define the attribute names and
552 values. For non-US usage, "colour" is recognized as synonymous with
553 "color".
554
555 Set the value to None to delete an attribute.
556
557 Colour translation is done as described in the doctext for palette().
558 """
559
560 redraw = False
561 for k, v in kwargs.iteritems():
562 k = k.lower()
563 if k == 'colour': k = 'color'
564
565 if 0 <= number < len(self.lines):
566 if self.lines[number] is not None:
567 for line in self.lines[number]:
568 getattr(line, "set_%s"%k)(v)
569 redraw = True
570 else:
571 if v is None:
572 del self.attributes[k]
573 else:
574 self.attributes[k] = v
575
576 if redraw: self.show(hardrefresh=False)
577
578
579 #def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, ganged=True):
580 def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, layout=None,ganged=True):
581 """
582 Set the panel layout.
583
584 rows and cols, if cols != 0, specify the number of rows and columns in
585 a regular layout. (Indexing of these panels in matplotlib is row-
586 major, i.e. column varies fastest.)
587
588 cols == 0 is interpreted as a retangular layout that accomodates
589 'rows' panels, e.g. rows == 6, cols == 0 is equivalent to
590 rows == 2, cols == 3.
591
592 0 <= n < rows*cols is interpreted as the 0-relative panel number in
593 the configuration specified by rows and cols to be added to the
594 current figure as its next 0-relative panel number (i). This allows
595 non-regular panel layouts to be constructed via multiple calls. Any
596 other value of n clears the plot and produces a rectangular array of
597 empty panels. The number of these may be limited by nplots.
598 """
599 if n < 0 and len(self.subplots):
600 self.figure.clear()
601 self.set_title()
602
603 if layout:
604 lef, bot, rig, top, wsp, hsp = layout
605 self.figure.subplots_adjust(
606 left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
607 del lef,bot,rig,top,wsp,hsp
608
609 if rows < 1: rows = 1
610
611 if cols <= 0:
612 i = int(sqrt(rows))
613 if i*i < rows: i += 1
614 cols = i
615
616 if i*(i-1) >= rows: i -= 1
617 rows = i
618
619 if 0 <= n < rows*cols:
620 i = len(self.subplots)
621
622 self.subplots.append({})
623
624 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
625 cols, n+1)
626 self.subplots[i]['lines'] = []
627
628 if i == 0: self.subplot(0)
629
630 self.rows = 0
631 self.cols = 0
632
633 else:
634 self.subplots = []
635
636 if nplots < 1 or rows*cols < nplots:
637 nplots = rows*cols
638 if ganged:
639 hsp,wsp = None,None
640 if rows > 1: hsp = 0.0001
641 if cols > 1: wsp = 0.0001
642 self.figure.subplots_adjust(wspace=wsp,hspace=hsp)
643 for i in range(nplots):
644 self.subplots.append({})
645 self.subplots[i]['lines'] = []
646 if not ganged:
647 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
648 cols, i+1)
649 if asaprcParams['plotter.axesformatting'] != 'mpl':
650 self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
651 else:
652 if i == 0:
653 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
654 cols, i+1)
655 if asaprcParams['plotter.axesformatting'] != 'mpl':
656
657 self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
658 else:
659 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
660 cols, i+1,
661 sharex=self.subplots[0]['axes'],
662 sharey=self.subplots[0]['axes'])
663
664 # Suppress tick labelling for interior subplots.
665 if i <= (rows-1)*cols - 1:
666 if i+cols < nplots:
667 # Suppress x-labels for frames width
668 # adjacent frames
669 for tick in self.subplots[i]['axes'].xaxis.majorTicks:
670 tick.label1On = False
671 self.subplots[i]['axes'].xaxis.label.set_visible(False)
672 if i%cols:
673 # Suppress y-labels for frames not in the left column.
674 for tick in self.subplots[i]['axes'].yaxis.majorTicks:
675 tick.label1On = False
676 self.subplots[i]['axes'].yaxis.label.set_visible(False)
677 # disable the first tick of [1:ncol-1] of the last row
678 #if i+1 < nplots:
679 # self.subplots[i]['axes'].xaxis.majorTicks[0].label1On = False
680 self.rows = rows
681 self.cols = cols
682 self.subplot(0)
683 del rows,cols,n,nplots,layout,ganged,i
684
685 def tidy(self):
686 # this needs to be exceuted after the first "refresh"
687 nplots = len(self.subplots)
688 if nplots == 1: return
689 for i in xrange(nplots):
690 ax = self.subplots[i]['axes']
691 if i%self.cols:
692 ax.xaxis.majorTicks[0].label1On = False
693 else:
694 if i != 0:
695 ax.yaxis.majorTicks[-1].label1On = False
696
697
698 def set_title(self, title=None):
699 """
700 Set the title of the plot window. Use the previous title if title is
701 omitted.
702 """
703 if title is not None:
704 self.title = title
705
706 self.figure.text(0.5, 0.95, self.title, horizontalalignment='center')
707
708
709 def show(self, hardrefresh=True):
710 """
711 Show graphics dependent on the current buffering state.
712 """
713 if not hardrefresh: return
714 if not self.buffering:
715 if self.loc is not None:
716 for sp in self.subplots:
717 lines = []
718 labels = []
719 i = 0
720 for line in sp['lines']:
721 i += 1
722 if line is not None:
723 lines.append(line[0])
724 lbl = line[0].get_label()
725 if lbl == '':
726 lbl = str(i)
727 labels.append(lbl)
728
729 if len(lines):
730 fp = FP(size=rcParams['legend.fontsize'])
731 fsz = fp.get_size_in_points() - len(lines)
732 fp.set_size(max(fsz,6))
733 sp['axes'].legend(tuple(lines), tuple(labels),
734 self.loc, prop=fp)
735 else:
736 sp['axes'].legend((' '))
737
738 from matplotlib.artist import setp
739 fpx = FP(size=rcParams['xtick.labelsize'])
740 xts = fpx.get_size_in_points()- (self.cols)/2
741 fpy = FP(size=rcParams['ytick.labelsize'])
742 yts = fpy.get_size_in_points() - (self.rows)/2
743 fpa = FP(size=rcParams['axes.labelsize'])
744 fpat = FP(size=rcParams['axes.titlesize'])
745 axsize = fpa.get_size_in_points()
746 tsize = fpat.get_size_in_points()-(self.cols)/2
747 for sp in self.subplots:
748 ax = sp['axes']
749 ax.title.set_size(tsize)
750 setp(ax.get_xticklabels(), fontsize=xts)
751 setp(ax.get_yticklabels(), fontsize=yts)
752 off = 0
753 if self.cols > 1: off = self.cols
754 ax.xaxis.label.set_size(axsize-off)
755 off = 0
756 if self.rows > 1: off = self.rows
757 ax.yaxis.label.set_size(axsize-off)
758
759 def subplot(self, i=None, inc=None):
760 """
761 Set the subplot to the 0-relative panel number as defined by one or
762 more invokations of set_panels().
763 """
764 l = len(self.subplots)
765 if l:
766 if i is not None:
767 self.i = i
768
769 if inc is not None:
770 self.i += inc
771
772 self.i %= l
773 self.axes = self.subplots[self.i]['axes']
774 self.lines = self.subplots[self.i]['lines']
775
776 def text(self, *args, **kwargs):
777 """
778 Add text to the figure.
779 """
780 self.figure.text(*args, **kwargs)
781 self.show()
782
783 def vline_with_label(self, x, y, label,
784 location='bottom', rotate=0.0, **kwargs):
785 """
786 Plot a vertical line with label.
787 It takes "world" values fo x and y.
788 """
789 ax = self.axes
790 # need this to suppress autoscaling during this function
791 self.axes.set_autoscale_on(False)
792 ymin = 0.0
793 ymax = 1.0
794 valign = 'center'
795 if location.lower() == 'top':
796 y = max(0.0, y)
797 elif location.lower() == 'bottom':
798 y = min(0.0, y)
799 lbloffset = 0.06
800 # a rough estimate for the bb of the text
801 if rotate > 0.0: lbloffset = 0.03*len(label)
802 peakoffset = 0.01
803 xy = None
804 xy0 = None
805 # matplotlib api change 0.98 is using transform now
806 if hasattr(ax.transData, "inverse_xy_tup"):
807 # get relative coords
808 xy0 = ax.transData.xy_tup((x,y))
809 xy = ax.transAxes.inverse_xy_tup(xy0)
810 else:
811 xy0 = ax.transData.transform((x,y))
812 # get relative coords
813 xy = ax.transAxes.inverted().transform(xy0)
814 if location.lower() == 'top':
815 ymax = 1.0-lbloffset
816 ymin = xy[1]+peakoffset
817 valign = 'bottom'
818 ylbl = ymax+0.01
819 elif location.lower() == 'bottom':
820 ymin = lbloffset
821 ymax = xy[1]-peakoffset
822 valign = 'top'
823 ylbl = ymin-0.01
824 trans = blended_transform_factory(ax.transData, ax.transAxes)
825 l = ax.axvline(x, ymin, ymax, color='black', **kwargs)
826 t = ax.text(x, ylbl ,label, verticalalignment=valign,
827 horizontalalignment='center',
828 rotation=rotate,transform = trans)
829 self.axes.set_autoscale_on(True)
Note: See TracBrowser for help on using the repository browser.