source: tags/asap2.3.1/python/asaplotbase.py @ 1561

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

fix for numpy >= 1.1

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