source: trunk/python/asaplotbase.py @ 1553

Last change on this file since 1553 was 1553, checked in by Malte Marquarding, 15 years ago

Fix for Ticket #157; numpy api changed for mask/data access in MaskedArray?

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