source: branches/casa-prerelease/pre-asap/python/asapplotter.py @ 2124

Last change on this file since 2124 was 2124, checked in by Kana Sugimoto, 13 years ago

merged a bug fix in trun (r2123)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.0 KB
Line 
1from asap.parameters import rcParams
2from asap.selector import selector
3from asap.scantable import scantable
4from asap.logging import asaplog, asaplog_post_dec
5import matplotlib.axes
6from matplotlib.font_manager import FontProperties
7from matplotlib.text import Text
8
9import re
10
11class asapplotter:
12    """
13    The ASAP plotter.
14    By default the plotter is set up to plot polarisations
15    'colour stacked' and scantables across panels.
16
17    .. note::
18
19        Currenly it only plots 'spectra' not Tsys or
20        other variables.
21
22    """
23    def __init__(self, visible=None , **kwargs):
24        self._visible = rcParams['plotter.gui']
25        if visible is not None:
26            self._visible = visible
27        self._plotter = self._newplotter(**kwargs)
28        # additional tool bar
29        self._plotter.figmgr.casabar=self._newcasabar()
30
31        self._panelling = None
32        self._stacking = None
33        self.set_panelling()
34        self.set_stacking()
35        self._rows = None
36        self._cols = None
37        self._autoplot = False
38        self._minmaxx = None
39        self._minmaxy = None
40        self._datamask = None
41        self._data = None
42        self._lmap = None
43        self._title = None
44        self._ordinate = None
45        self._abcissa = None
46        self._abcunit = None
47        self._usermask = []
48        self._maskselection = None
49        self._selection = selector()
50        self._hist = rcParams['plotter.histogram']
51        self._fp = FontProperties()
52        self._margins = self.set_margin(refresh=False)
53        self._offset = None
54        self._startrow = 0
55        self._ipanel = -1
56        self._panelrows = []
57        self._headtext={'string': None, 'textobj': None}
58
59    def _translate(self, instr):
60        keys = "s b i p t r".split()
61        if isinstance(instr, str):
62            for key in keys:
63                if instr.lower().startswith(key):
64                    return key
65        return None
66
67    def _newplotter(self, **kwargs):
68        backend=matplotlib.get_backend()
69        if not self._visible:
70            from asap.asaplot import asaplot
71        elif backend == 'TkAgg':
72            from asap.asaplotgui import asaplotgui as asaplot
73        elif backend == 'Qt4Agg':
74            from asap.asaplotgui_qt4 import asaplotgui as asaplot
75        elif backend == 'GTkAgg':
76            from asap.asaplotgui_gtk import asaplotgui as asaplot
77        else:
78            from asap.asaplot import asaplot
79        return asaplot(**kwargs)
80
81    def _newcasabar(self):
82        backend=matplotlib.get_backend()
83        if self._visible and backend == "TkAgg":
84            from asap.casatoolbar import CustomToolbarTkAgg
85            return CustomToolbarTkAgg(self)
86            #from asap.casatoolbar import CustomFlagToolbarTkAgg
87            #return CustomFlagToolbarTkAgg(self)
88        return None
89
90    @asaplog_post_dec
91    def plot(self, scan=None):
92        """
93        Plot a scantable.
94        Parameters:
95            scan:   a scantable
96        Note:
97            If a scantable was specified in a previous call
98            to plot, no argument has to be given to 'replot'
99            NO checking is done that the abcissas of the scantable
100            are consistent e.g. all 'channel' or all 'velocity' etc.
101        """
102        self._startrow = 0
103        self._ipanel = -1
104        self._reset_header()
105        if self._plotter.is_dead:
106            if hasattr(self._plotter.figmgr,'casabar'):
107                del self._plotter.figmgr.casabar
108            self._plotter = self._newplotter()
109            self._plotter.figmgr.casabar=self._newcasabar()
110        if self._plotter.figmgr.casabar:
111            self._plotter.figmgr.casabar.set_pagecounter(1)
112        self._panelrows = []
113        self._plotter.hold()
114        #self._plotter.clear()
115        if not self._data and not scan:
116            msg = "Input is not a scantable"
117            raise TypeError(msg)
118        if scan:
119            self.set_data(scan, refresh=False)
120        self._plot(self._data)
121        if self._minmaxy is not None:
122            self._plotter.set_limits(ylim=self._minmaxy)
123        if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.enable_button()
124        self._plotter.release()
125        self._plotter.tidy()
126        self._plotter.show(hardrefresh=False)
127        return
128
129    def gca(self):
130        return self._plotter.figure.gca()
131
132    def refresh(self):
133        """Do a soft refresh"""
134        self._plotter.figure.show()
135
136    def create_mask(self, nwin=1, panel=0, color=None):
137        """
138        Interactively define a mask. It retruns a mask that is equivalent to
139        the one created manually with scantable.create_mask.
140        Parameters:
141            nwin:       The number of mask windows to create interactively
142                        default is 1.
143            panel:      Which panel to use for mask selection. This is useful
144                        if different IFs are spread over panels (default 0)
145        """
146        if self._data is None:
147            return []
148        outmask = []
149        self._plotter.subplot(panel)
150        xmin, xmax = self._plotter.axes.get_xlim()
151        marg = 0.05*(xmax-xmin)
152        self._plotter.axes.set_xlim(xmin-marg, xmax+marg)
153        self.refresh()
154
155        def cleanup(lines=False, texts=False, refresh=False):
156            if lines:
157                del self._plotter.axes.lines[-1]
158            if texts:
159                del self._plotter.axes.texts[-1]
160            if refresh:
161                self.refresh()
162
163        for w in xrange(nwin):
164            wpos = []
165            self.text(0.05,1.0, "Add start boundary",
166                      coords="relative", fontsize=10)
167            point = self._plotter.get_point()
168            cleanup(texts=True)
169            if point is None:
170                continue
171            wpos.append(point[0])
172            self.axvline(wpos[0], color=color)
173            self.text(0.05,1.0, "Add end boundary", coords="relative", fontsize=10)
174            point = self._plotter.get_point()
175            cleanup(texts=True, lines=True)
176            if point is None:
177                self.refresh()
178                continue
179            wpos.append(point[0])
180            self.axvspan(wpos[0], wpos[1], alpha=0.1,
181                         edgecolor=color, facecolor=color)
182            ymin, ymax = self._plotter.axes.get_ylim()
183            outmask.append(wpos)
184
185        self._plotter.axes.set_xlim(xmin, xmax)
186        self.refresh()
187        if len(outmask) > 0:
188            return self._data.create_mask(*outmask)
189        return []
190
191    # forwards to matplotlib axes
192    def text(self, *args, **kwargs):
193        if kwargs.has_key("interactive"):
194            if kwargs.pop("interactive"):
195                pos = self._plotter.get_point()
196                args = tuple(pos)+args
197        self._axes_callback("text", *args, **kwargs)
198
199    text.__doc__ = matplotlib.axes.Axes.text.__doc__
200
201    def arrow(self, *args, **kwargs):
202        if kwargs.has_key("interactive"):
203            if kwargs.pop("interactive"):
204                pos = self._plotter.get_region()
205                dpos = (pos[0][0], pos[0][1],
206                        pos[1][0]-pos[0][0],
207                        pos[1][1] - pos[0][1])
208                args = dpos + args
209        self._axes_callback("arrow", *args, **kwargs)
210
211    arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
212
213    def annotate(self, text, xy=None, xytext=None, **kwargs):
214        if kwargs.has_key("interactive"):
215            if kwargs.pop("interactive"):
216                xy = self._plotter.get_point()
217                xytext = self._plotter.get_point()
218        if not kwargs.has_key("arrowprops"):
219            kwargs["arrowprops"] = dict(arrowstyle="->")
220        self._axes_callback("annotate", text, xy, xytext, **kwargs)
221
222    annotate.__doc__ = matplotlib.axes.Axes.annotate.__doc__
223
224    def axvline(self, *args, **kwargs):
225        if kwargs.has_key("interactive"):
226            if kwargs.pop("interactive"):
227                pos = self._plotter.get_point()
228                args = (pos[0],)+args
229        self._axes_callback("axvline", *args, **kwargs)
230
231    axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
232
233    def axhline(self, *args, **kwargs):
234        if kwargs.has_key("interactive"):
235            if kwargs.pop("interactive"):
236                pos = self._plotter.get_point()
237                args = (pos[1],)+args
238        self._axes_callback("axhline", *args, **kwargs)
239
240    axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
241
242    def axvspan(self, *args, **kwargs):
243        if kwargs.has_key("interactive"):
244            if kwargs.pop("interactive"):
245                pos = self._plotter.get_region()
246                dpos = (pos[0][0], pos[1][0])
247                args = dpos + args
248        self._axes_callback("axvspan", *args, **kwargs)
249        # hack to preventy mpl from redrawing the patch
250        # it seem to convert the patch into lines on every draw.
251        # This doesn't happen in a test script???
252        #del self._plotter.axes.patches[-1]
253
254    axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
255
256    def axhspan(self, *args, **kwargs):
257        if kwargs.has_key("interactive"):
258            if kwargs.pop("interactive"):
259                pos = self._plotter.get_region()
260                dpos = (pos[0][1], pos[1][1])
261                args = dpos + args
262        self._axes_callback("axhspan", *args, **kwargs)
263        # hack to preventy mpl from redrawing the patch
264        # it seem to convert the patch into lines on every draw.
265        # This doesn't happen in a test script???
266        #del self._plotter.axes.patches[-1]
267
268    axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
269
270    def _axes_callback(self, axesfunc, *args, **kwargs):
271        panel = 0
272        if kwargs.has_key("panel"):
273            panel = kwargs.pop("panel")
274        coords = None
275        if kwargs.has_key("coords"):
276            coords = kwargs.pop("coords")
277            if coords.lower() == 'world':
278                kwargs["transform"] = self._plotter.axes.transData
279            elif coords.lower() == 'relative':
280                kwargs["transform"] = self._plotter.axes.transAxes
281        self._plotter.subplot(panel)
282        self._plotter.axes.set_autoscale_on(False)
283        getattr(self._plotter.axes, axesfunc)(*args, **kwargs)
284        self._plotter.show(False)
285        self._plotter.axes.set_autoscale_on(True)
286    # end matplotlib.axes fowarding functions
287
288    @asaplog_post_dec
289    def set_data(self, scan, refresh=True):
290        """
291        Set a scantable to plot.
292        Parameters:
293            scan:      a scantable
294            refresh:   True (default) or False. If True, the plot is
295                       replotted based on the new parameter setting(s).
296                       Otherwise,the parameter(s) are set without replotting.
297        Note:
298           The user specified masks and data selections will be reset
299           if a new scantable is set. This method should be called before
300           setting data selections (set_selection) and/or masks (set_mask).
301        """
302        from asap import scantable
303        if isinstance(scan, scantable):
304            if self._data is not None:
305                if scan != self._data:
306                    del self._data
307                    self._data = scan
308                    # reset
309                    self._reset()
310                    msg = "A new scantable is set to the plotter. "\
311                          "The masks and data selections are reset."
312                    asaplog.push( msg )
313            else:
314                self._data = scan
315                self._reset()
316        else:
317            msg = "Input is not a scantable"
318            raise TypeError(msg)
319
320        # ranges become invalid when unit changes
321        if self._abcunit and self._abcunit != self._data.get_unit():
322            self._minmaxx = None
323            self._minmaxy = None
324            self._abcunit = self._data.get_unit()
325            self._datamask = None
326        if refresh: self.plot()
327
328    @asaplog_post_dec
329    def set_mode(self, stacking=None, panelling=None, refresh=True):
330        """
331        Set the plots look and feel, i.e. what you want to see on the plot.
332        Parameters:
333            stacking:     tell the plotter which variable to plot
334                          as line colour overlays (default 'pol')
335            panelling:    tell the plotter which variable to plot
336                          across multiple panels (default 'scan'
337            refresh:      True (default) or False. If True, the plot is
338                          replotted based on the new parameter setting(s).
339                          Otherwise,the parameter(s) are set without replotting.
340        Note:
341            Valid modes are:
342                 'beam' 'Beam' 'b':     Beams
343                 'if' 'IF' 'i':         IFs
344                 'pol' 'Pol' 'p':       Polarisations
345                 'scan' 'Scan' 's':     Scans
346                 'time' 'Time' 't':     Times
347                 'row' 'Row' 'r':       Rows
348            When either 'stacking' or 'panelling' is set to 'row',
349            the other parameter setting is ignored.
350        """
351        msg = "Invalid mode"
352        if not self.set_panelling(panelling) or \
353               not self.set_stacking(stacking):
354            raise TypeError(msg)
355        #if self._panelling == 'r':
356        #    self._stacking = '_r'
357        #if self._stacking == 'r':
358        #    self._panelling = '_r'
359        if refresh and self._data: self.plot(self._data)
360        return
361
362    def set_panelling(self, what=None):
363        """Set the 'panelling' mode i.e. which type of spectra should be
364        spread across different panels.
365        """
366
367        mode = what
368        if mode is None:
369             mode = rcParams['plotter.panelling']
370        md = self._translate(mode)
371        if md:
372            self._panelling = md
373            self._title = None
374            #if md == 'r':
375            #    self._stacking = '_r'
376            # you need to reset counters for multi page plotting
377            self._reset_counters()
378            return True
379        return False
380
381    def set_layout(self,rows=None,cols=None,refresh=True):
382        """
383        Set the multi-panel layout, i.e. how many rows and columns plots
384        are visible.
385        Parameters:
386             rows:   The number of rows of plots
387             cols:   The number of columns of plots
388             refresh:  True (default) or False. If True, the plot is
389                       replotted based on the new parameter setting(s).
390                       Otherwise,the parameter(s) are set without replotting.
391        Note:
392             If no argument is given, the potter reverts to its auto-plot
393             behaviour.
394        """
395        self._rows = rows
396        self._cols = cols
397        if refresh and self._data: self.plot(self._data)
398        return
399
400    def set_stacking(self, what=None):
401        """Set the 'stacking' mode i.e. which type of spectra should be
402        overlayed.
403        """
404        mode = what
405        if mode is None:
406             mode = rcParams['plotter.stacking']
407        md = self._translate(mode)
408        if md:
409            self._stacking = md
410            self._lmap = None
411            #if md == 'r':
412            #    self._panelling = '_r'
413            # you need to reset counters for multi page plotting
414            self._reset_counters()
415            return True
416        return False
417
418    def _reset_counters(self):
419        self._startrow = 0
420        self._ipanel = -1
421        self._panelrows = []
422
423    def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True, offset=None):
424        """
425        Set the range of interest on the abcissa of the plot
426        Parameters:
427            [x,y]start,[x,y]end:  The start and end points of the 'zoom' window
428            refresh:  True (default) or False. If True, the plot is
429                      replotted based on the new parameter setting(s).
430                      Otherwise,the parameter(s) are set without replotting.
431            offset:   shift the abcissa by the given amount. The abcissa label will
432                      have '(relative)' appended to it.
433        Note:
434            These become non-sensical when the unit changes.
435            use plotter.set_range() without parameters to reset
436
437        """
438        self._offset = offset
439        if xstart is None and xend is None:
440            self._minmaxx = None
441        else:
442            self._minmaxx = [xstart,xend]
443        if ystart is None and yend is None:
444            self._minmaxy = None
445        else:
446            self._minmaxy = [ystart,yend]
447        if refresh and self._data: self.plot(self._data)
448        return
449
450    def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
451        """
452        Specify a mapping for the legend instead of using the default
453        indices:
454        Parameters:
455            mp:        a list of 'strings'. This should have the same length
456                       as the number of elements on the legend and then maps
457                       to the indeces in order. It is possible to uses latex
458                       math expression. These have to be enclosed in r'',
459                       e.g. r'$x^{2}$'
460            fontsize:  The font size of the label (default None)
461            mode:      where to display the legend
462                       Any other value for loc else disables the legend:
463                        0: auto
464                        1: upper right
465                        2: upper left
466                        3: lower left
467                        4: lower right
468                        5: right
469                        6: center left
470                        7: center right
471                        8: lower center
472                        9: upper center
473                        10: center
474            refresh:    True (default) or False. If True, the plot is
475                        replotted based on the new parameter setting(s).
476                        Otherwise,the parameter(s) are set without replotting.
477
478        Example:
479             If the data has two IFs/rest frequencies with index 0 and 1
480             for CO and SiO:
481             plotter.set_stacking('i')
482             plotter.set_legend(['CO','SiO'])
483             plotter.plot()
484             plotter.set_legend([r'$^{12}CO$', r'SiO'])
485        """
486        self._lmap = mp
487        self._plotter.legend(mode)
488        if isinstance(fontsize, int):
489            from matplotlib import rc as rcp
490            rcp('legend', fontsize=fontsize)
491        if refresh and self._data: self.plot(self._data)
492        return
493
494    def set_title(self, title=None, fontsize=None, refresh=True):
495        """
496        Set the title of the plot. If multiple panels are plotted,
497        multiple titles have to be specified.
498        Parameters:
499            refresh:    True (default) or False. If True, the plot is
500                        replotted based on the new parameter setting(s).
501                        Otherwise,the parameter(s) are set without replotting.
502        Example:
503             # two panels are visible on the plotter
504             plotter.set_title(["First Panel","Second Panel"])
505        """
506        self._title = title
507        if isinstance(fontsize, int):
508            from matplotlib import rc as rcp
509            rcp('axes', titlesize=fontsize)
510        if refresh and self._data: self.plot(self._data)
511        return
512
513    def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
514        """
515        Set the y-axis label of the plot. If multiple panels are plotted,
516        multiple labels have to be specified.
517        Parameters:
518            ordinate:    a list of ordinate labels. None (default) let
519                         data determine the labels
520            refresh:     True (default) or False. If True, the plot is
521                         replotted based on the new parameter setting(s).
522                         Otherwise,the parameter(s) are set without replotting.
523        Example:
524             # two panels are visible on the plotter
525             plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
526        """
527        self._ordinate = ordinate
528        if isinstance(fontsize, int):
529            from matplotlib import rc as rcp
530            rcp('axes', labelsize=fontsize)
531            rcp('ytick', labelsize=fontsize)
532        if refresh and self._data: self.plot(self._data)
533        return
534
535    def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
536        """
537        Set the x-axis label of the plot. If multiple panels are plotted,
538        multiple labels have to be specified.
539        Parameters:
540            abcissa:     a list of abcissa labels. None (default) let
541                         data determine the labels
542            refresh:     True (default) or False. If True, the plot is
543                         replotted based on the new parameter setting(s).
544                         Otherwise,the parameter(s) are set without replotting.
545        Example:
546             # two panels are visible on the plotter
547             plotter.set_ordinate(["First X-Axis","Second X-Axis"])
548        """
549        self._abcissa = abcissa
550        if isinstance(fontsize, int):
551            from matplotlib import rc as rcp
552            rcp('axes', labelsize=fontsize)
553            rcp('xtick', labelsize=fontsize)
554        if refresh and self._data: self.plot(self._data)
555        return
556
557    def set_colors(self, colmap, refresh=True):
558        """
559        Set the colours to be used. The plotter will cycle through
560        these colours when lines are overlaid (stacking mode).
561        Parameters:
562            colmap:     a list of colour names
563            refresh:    True (default) or False. If True, the plot is
564                        replotted based on the new parameter setting(s).
565                        Otherwise,the parameter(s) are set without replotting.
566        Example:
567             plotter.set_colors("red green blue")
568             # If for example four lines are overlaid e.g I Q U V
569             # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
570             # and 'V' will be 'red' again.
571        """
572        if isinstance(colmap,str):
573            colmap = colmap.split()
574        self._plotter.palette(0, colormap=colmap)
575        if refresh and self._data: self.plot(self._data)
576
577    # alias for english speakers
578    set_colours = set_colors
579
580    def set_histogram(self, hist=True, linewidth=None, refresh=True):
581        """
582        Enable/Disable histogram-like plotting.
583        Parameters:
584            hist:        True (default) or False. The fisrt default
585                         is taken from the .asaprc setting
586                         plotter.histogram
587            refresh:     True (default) or False. If True, the plot is
588                         replotted based on the new parameter setting(s).
589                         Otherwise,the parameter(s) are set without replotting.
590        """
591        self._hist = hist
592        if isinstance(linewidth, float) or isinstance(linewidth, int):
593            from matplotlib import rc as rcp
594            rcp('lines', linewidth=linewidth)
595        if refresh and self._data: self.plot(self._data)
596
597    def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
598        """
599        Set the linestyles to be used. The plotter will cycle through
600        these linestyles when lines are overlaid (stacking mode) AND
601        only one color has been set.
602        Parameters:
603             linestyles:     a list of linestyles to use.
604                             'line', 'dashed', 'dotted', 'dashdot',
605                             'dashdotdot' and 'dashdashdot' are
606                             possible
607            refresh:         True (default) or False. If True, the plot is
608                             replotted based on the new parameter setting(s).
609                             Otherwise,the parameter(s) are set without replotting.
610        Example:
611             plotter.set_colors("black")
612             plotter.set_linestyles("line dashed dotted dashdot")
613             # If for example four lines are overlaid e.g I Q U V
614             # 'I' will be 'solid', 'Q' will be 'dashed',
615             # U will be 'dotted' and 'V' will be 'dashdot'.
616        """
617        if isinstance(linestyles,str):
618            linestyles = linestyles.split()
619        self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
620        if isinstance(linewidth, float) or isinstance(linewidth, int):
621            from matplotlib import rc as rcp
622            rcp('lines', linewidth=linewidth)
623        if refresh and self._data: self.plot(self._data)
624
625    def set_font(self, refresh=True,**kwargs):
626        """
627        Set font properties.
628        Parameters:
629            family:    one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
630            style:     one of 'normal' (or 'roman'), 'italic'  or 'oblique'
631            weight:    one of 'normal or 'bold'
632            size:      the 'general' font size, individual elements can be adjusted
633                       seperately
634            refresh:   True (default) or False. If True, the plot is
635                       replotted based on the new parameter setting(s).
636                       Otherwise,the parameter(s) are set without replotting.
637        """
638        from matplotlib import rc as rcp
639        fdict = {}
640        for k,v in kwargs.iteritems():
641            if v:
642                fdict[k] = v
643        self._fp = FontProperties(**fdict)
644        if refresh and self._data: self.plot(self._data)
645
646    def set_margin(self,margin=[],refresh=True):
647        """
648        Set margins between subplots and plot edges.
649        Parameters:
650            margin:   a list of margins in figure coordinate (0-1),
651                      i.e., fraction of the figure width or height.
652                      The order of elements should be:
653                      [left, bottom, right, top, horizontal space btw panels,
654                      vertical space btw panels].
655            refresh:  True (default) or False. If True, the plot is
656                      replotted based on the new parameter setting(s).
657                      Otherwise,the parameter(s) are set without replotting.
658        Note
659        * When margin is not specified, the values are reset to the defaults
660          of matplotlib.
661        * If any element is set to be None, the current value is adopted.
662        """
663        if margin == []: self._margins=self._reset_margin()
664        else:
665            self._margins=[None]*6
666            self._margins[0:len(margin)]=margin
667        #print "panel margin set to ",self._margins
668        if refresh and self._data: self.plot(self._data)
669
670    def _reset_margin(self):
671        ks=map(lambda x: 'figure.subplot.'+x,
672               ['left','bottom','right','top','hspace','wspace'])
673        return map(matplotlib.rcParams.get,ks)
674
675    def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
676                   location=None):
677        """
678        Plot a line catalog.
679        Parameters:
680            linecat:      the linecatalog to plot
681            doppler:      the velocity shift to apply to the frequencies
682            deltachan:    the number of channels to include each side of the
683                          line to determine a local maximum/minimum
684            rotate:       the rotation (in degrees) for the text label (default 90.0)
685            location:     the location of the line annotation from the 'top',
686                          'bottom' or alternate (None - the default)
687        Notes:
688        If the spectrum is flagged no line will be drawn in that location.
689        """
690        if not self._data:
691            raise RuntimeError("No scantable has been plotted yet.")
692        from asap._asap import linecatalog
693        if not isinstance(linecat, linecatalog):
694            raise ValueError("'linecat' isn't of type linecatalog.")
695        if not self._data.get_unit().endswith("Hz"):
696            raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
697        from numpy import ma
698        for j in range(len(self._plotter.subplots)):
699            self._plotter.subplot(j)
700            lims = self._plotter.axes.get_xlim()
701            for row in range(linecat.nrow()):
702                # get_frequency returns MHz
703                base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
704                restf = linecat.get_frequency(row)/base[self._data.get_unit()]
705                c = 299792.458
706                freq = restf*(1.0-doppler/c)
707                if lims[0] < freq < lims[1]:
708                    if location is None:
709                        loc = 'bottom'
710                        if row%2: loc='top'
711                    else: loc = location
712                    maxys = []
713                    for line in self._plotter.axes.lines:
714                        v = line._x
715                        asc = v[0] < v[-1]
716
717                        idx = None
718                        if not asc:
719                            if v[len(v)-1] <= freq <= v[0]:
720                                i = len(v)-1
721                                while i>=0 and v[i] < freq:
722                                    idx = i
723                                    i-=1
724                        else:
725                           if v[0] <= freq <= v[len(v)-1]:
726                                i = 0
727                                while  i<len(v) and v[i] < freq:
728                                    idx = i
729                                    i+=1
730                        if idx is not None:
731                            lower = idx - deltachan
732                            upper = idx + deltachan
733                            if lower < 0: lower = 0
734                            if upper > len(v): upper = len(v)
735                            s = slice(lower, upper)
736                            y = line._y[s]
737                            maxy = ma.maximum(y)
738                            if isinstance( maxy, float):
739                                maxys.append(maxy)
740                    if len(maxys):
741                        peak = max(maxys)
742                        if peak > self._plotter.axes.get_ylim()[1]:
743                            loc = 'bottom'
744                    else:
745                        continue
746                    self._plotter.vline_with_label(freq, peak,
747                                                   linecat.get_name(row),
748                                                   location=loc, rotate=rotate)
749        self._plotter.show(hardrefresh=False)
750
751
752    def save(self, filename=None, orientation=None, dpi=None):
753        """
754        Save the plot to a file. The known formats are 'png', 'ps', 'eps'.
755        Parameters:
756             filename:    The name of the output file. This is optional
757                          and autodetects the image format from the file
758                          suffix. If non filename is specified a file
759                          called 'yyyymmdd_hhmmss.png' is created in the
760                          current directory.
761             orientation: optional parameter for postscript only (not eps).
762                          'landscape', 'portrait' or None (default) are valid.
763                          If None is choosen for 'ps' output, the plot is
764                          automatically oriented to fill the page.
765             dpi:         The dpi of the output non-ps plot
766        """
767        self._plotter.save(filename,orientation,dpi)
768        return
769
770    @asaplog_post_dec
771    def set_mask(self, mask=None, selection=None, refresh=True):
772        """
773        Set a plotting mask for a specific polarization.
774        This is useful for masking out "noise" Pangle outside a source.
775        Parameters:
776             mask:           a mask from scantable.create_mask
777             selection:      the spectra to apply the mask to.
778             refresh:        True (default) or False. If True, the plot is
779                             replotted based on the new parameter setting(s).
780                             Otherwise,the parameter(s) are set without replotting.
781        Example:
782             select = selector()
783             select.setpolstrings("Pangle")
784             plotter.set_mask(mymask, select)
785        """
786        if not self._data:
787            msg = "Can only set mask after a first call to plot()"
788            raise RuntimeError(msg)
789        if len(mask):
790            if isinstance(mask, list) or isinstance(mask, tuple):
791                self._usermask = array(mask)
792            else:
793                self._usermask = mask
794        if mask is None and selection is None:
795            self._usermask = []
796            self._maskselection = None
797        if isinstance(selection, selector):
798            self._maskselection = {'b': selection.get_beams(),
799                                   's': selection.get_scans(),
800                                   'i': selection.get_ifs(),
801                                   'p': selection.get_pols(),
802                                   't': [] }
803        else:
804            self._maskselection = None
805        if refresh: self.plot(self._data)
806
807    def _slice_indeces(self, data):
808        mn = self._minmaxx[0]
809        mx = self._minmaxx[1]
810        asc = data[0] < data[-1]
811        start=0
812        end = len(data)-1
813        inc = 1
814        if not asc:
815            start = len(data)-1
816            end = 0
817            inc = -1
818        # find min index
819        #while start > 0 and data[start] < mn:
820        #    start+= inc
821        minind=start
822        for ind in xrange(start,end+inc,inc):
823            if data[ind] > mn: break
824            minind=ind
825        # find max index
826        #while end > 0 and data[end] > mx:
827        #    end-=inc
828        #if end > 0: end +=1
829        maxind=end
830        for ind in xrange(end,start-inc,-inc):
831            if data[ind] < mx: break
832            maxind=ind
833        start=minind
834        end=maxind
835        if start > end:
836            return end,start+1
837        elif start < end:
838            return start,end+1
839        else:
840            return start,end
841
842    def _reset(self):
843        self._usermask = []
844        self._usermaskspectra = None
845        self._offset = None
846        self.set_selection(None, False)
847        self._reset_header()
848
849    def _reset_header(self):
850        self._headtext={'string': None, 'textobj': None}
851
852    def _plot(self, scan):
853        savesel = scan.get_selection()
854        sel = savesel +  self._selection
855        order = self._get_sortstring([self._panelling,self._stacking])
856        if order:
857            sel.set_order(order)
858        scan.set_selection(sel)
859        d = {'b': scan.getbeam, 's': scan.getscan,
860             'i': scan.getif, 'p': scan.getpol, 't': scan.get_time,
861             'r': int}#, '_r': int}
862
863        polmodes = dict(zip(self._selection.get_pols(),
864                            self._selection.get_poltypes()))
865        # this returns either a tuple of numbers or a length  (ncycles)
866        # convert this into lengths
867        n0,nstack0 = self._get_selected_n(scan)
868        if isinstance(n0, int): n = n0
869        else: n = len(n0)
870        if isinstance(nstack0, int): nstack = nstack0
871        else: nstack = len(nstack0)
872        # In case of row stacking
873        rowstack = False
874        titlemode = self._panelling
875        if self._stacking == "r" and self._panelling != "r":
876            rowstack = True
877            titlemode = '_r'
878        nptot = n
879        maxpanel, maxstack = 16,16
880        if nstack > maxstack:
881            msg ="Scan to be overlayed contains more than %d selections.\n" \
882                  "Selecting first %d selections..." % (maxstack, maxstack)
883            asaplog.push(msg)
884            asaplog.post('WARN')
885            nstack = min(nstack,maxstack)
886        #n = min(n-self._ipanel-1,maxpanel)
887        n = n-self._ipanel-1
888
889        ganged = False
890        if n > 1:
891            ganged = rcParams['plotter.ganged']
892            if self._panelling == 'i':
893                ganged = False
894            if self._rows and self._cols:
895                n = min(n,self._rows*self._cols)
896                self._plotter.set_panels(rows=self._rows,cols=self._cols,
897                                         nplots=n,margin=self._margins,ganged=ganged)
898            else:
899                n = min(n,maxpanel)
900                self._plotter.set_panels(rows=n,cols=0,nplots=n,margin=self._margins,ganged=ganged)
901        else:
902            self._plotter.set_panels(margin=self._margins)
903        #r = 0
904        r = self._startrow
905        nr = scan.nrow()
906        a0,b0 = -1,-1
907        allxlim = []
908        allylim = []
909        #newpanel=True
910        newpanel=False
911        panelcount,stackcount = 0,0
912        # If this is not the first page
913        if r > 0:
914            # panelling value of the prev page
915            a0 = d[self._panelling](r-1)
916            # set the initial stackcount large not to plot
917            # the start row automatically
918            stackcount = nstack
919
920        while r < nr:
921            a = d[self._panelling](r)
922            b = d[self._stacking](r)
923            if a > a0 and panelcount < n:
924                if n > 1:
925                    self._plotter.subplot(panelcount)
926                self._plotter.palette(0)
927                #title
928                xlab = self._abcissa and self._abcissa[panelcount] \
929                       or scan._getabcissalabel()
930                if self._offset and not self._abcissa:
931                    xlab += " (relative)"
932                ylab = self._ordinate and self._ordinate[panelcount] \
933                       or scan._get_ordinate_label()
934                self._plotter.set_axes('xlabel', xlab)
935                self._plotter.set_axes('ylabel', ylab)
936                #lbl = self._get_label(scan, r, self._panelling, self._title)
937                lbl = self._get_label(scan, r, titlemode, self._title)
938                if isinstance(lbl, list) or isinstance(lbl, tuple):
939                    if 0 <= panelcount < len(lbl):
940                        lbl = lbl[panelcount]
941                    else:
942                        # get default label
943                        #lbl = self._get_label(scan, r, self._panelling, None)
944                        lbl = self._get_label(scan, r, titlemode, None)
945                self._plotter.set_axes('title',lbl)
946                newpanel = True
947                stackcount = 0
948                panelcount += 1
949                # save the start row to plot this panel for future revisit.
950                if self._panelling != 'r' and \
951                       len(self._panelrows) < self._ipanel+1+panelcount:
952                    self._panelrows += [r]
953                   
954            #if (b > b0 or newpanel) and stackcount < nstack:
955            if stackcount < nstack and (newpanel or rowstack or (a == a0 and b > b0)):
956                y = []
957                if len(polmodes):
958                    y = scan._getspectrum(r, polmodes[scan.getpol(r)])
959                else:
960                    y = scan._getspectrum(r)
961                # flag application
962                mr = scan._getflagrow(r)
963                from numpy import ma, array
964                if mr:
965                    y = ma.masked_array(y,mask=mr)
966                else:
967                    m = scan._getmask(r)
968                    from numpy import logical_not, logical_and
969                    if self._maskselection and len(self._usermask) == len(m):
970                        if d[self._stacking](r) in self._maskselection[self._stacking]:
971                            m = logical_and(m, self._usermask)
972                    y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
973
974                x = array(scan._getabcissa(r))
975                if self._offset:
976                    x += self._offset
977                if self._minmaxx is not None:
978                    s,e = self._slice_indeces(x)
979                    x = x[s:e]
980                    y = y[s:e]
981                if len(x) > 1024 and rcParams['plotter.decimate']:
982                    fac = len(x)/1024
983                    x = x[::fac]
984                    y = y[::fac]
985                llbl = self._get_label(scan, r, self._stacking, self._lmap)
986                if isinstance(llbl, list) or isinstance(llbl, tuple):
987                    if 0 <= stackcount < len(llbl):
988                        # use user label
989                        llbl = llbl[stackcount]
990                    else:
991                        # get default label
992                        llbl = self._get_label(scan, r, self._stacking, None)
993                self._plotter.set_line(label=llbl)
994                plotit = self._plotter.plot
995                if self._hist: plotit = self._plotter.hist
996                if len(x) > 0 and not mr:
997                    plotit(x,y)
998                    xlim= self._minmaxx or [min(x),max(x)]
999                    allxlim += xlim
1000                    ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
1001                    allylim += ylim
1002                else:
1003                    xlim = self._minmaxx or []
1004                    allxlim += xlim
1005                    ylim= self._minmaxy or []
1006                    allylim += ylim
1007                stackcount += 1
1008                a0=a
1009                b0=b
1010                # last in colour stack -> autoscale x
1011                if stackcount == nstack and len(allxlim) > 0:
1012                    allxlim.sort()
1013                    self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
1014                    if ganged:
1015                        allxlim = [allxlim[0],allxlim[-1]]
1016                    else:
1017                        # clear
1018                        allxlim =[]
1019
1020            newpanel = False
1021            #a0=a
1022            #b0=b
1023            # ignore following rows
1024            if (panelcount == n and stackcount == nstack) or (r == nr-1):
1025                # last panel -> autoscale y if ganged
1026                #if rcParams['plotter.ganged'] and len(allylim) > 0:
1027                if ganged and len(allylim) > 0:
1028                    allylim.sort()
1029                    self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
1030                break
1031            r+=1 # next row
1032
1033        # save the current counter for multi-page plotting
1034        self._startrow = r+1
1035        self._ipanel += panelcount
1036        if self._plotter.figmgr.casabar:
1037            if self._ipanel >= nptot-1:
1038                self._plotter.figmgr.casabar.disable_next()
1039            else:
1040                self._plotter.figmgr.casabar.enable_next()
1041            if self._ipanel + 1 - panelcount > 0:
1042                self._plotter.figmgr.casabar.enable_prev()
1043            else:
1044                self._plotter.figmgr.casabar.disable_prev()
1045
1046        #reset the selector to the scantable's original
1047        scan.set_selection(savesel)
1048
1049        #temporary switch-off for older matplotlib
1050        #if self._fp is not None:
1051        if self._fp is not None and getattr(self._plotter.figure,'findobj',False):
1052            for o in self._plotter.figure.findobj(Text):
1053                o.set_fontproperties(self._fp)
1054
1055    def _get_sortstring(self, lorders):
1056        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
1057              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME', 'r':None, '_r':None }
1058        if not (type(lorders) == list) and not (type(lorders) == tuple):
1059            return None
1060        if len(lorders) > 0:
1061            lsorts = []
1062            for order in lorders:
1063                if order == "r":
1064                    # don't sort if row panelling/stacking
1065                    return None
1066                ssort = d0[order]
1067                if ssort:
1068                    lsorts.append(ssort)
1069            return lsorts
1070        return None
1071
1072    def set_selection(self, selection=None, refresh=True, **kw):
1073        """
1074        Parameters:
1075            selection:  a selector object (default unset the selection)
1076            refresh:    True (default) or False. If True, the plot is
1077                        replotted based on the new parameter setting(s).
1078                        Otherwise,the parameter(s) are set without replotting.
1079        """
1080        if selection is None:
1081            # reset
1082            if len(kw) == 0:
1083                self._selection = selector()
1084            else:
1085                # try keywords
1086                for k in kw:
1087                    if k not in selector.fields:
1088                        raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
1089                self._selection = selector(**kw)
1090        elif isinstance(selection, selector):
1091            self._selection = selection
1092        else:
1093            raise TypeError("'selection' is not of type selector")
1094
1095        order = self._get_sortstring([self._panelling,self._stacking])
1096        if order:
1097            self._selection.set_order(order)
1098        if refresh and self._data: self.plot(self._data)
1099
1100    def _get_selected_n(self, scan):
1101        d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
1102             'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle,
1103             'r': scan.nrow}#, '_r': False}
1104        d2 = { 'b': self._selection.get_beams(),
1105               's': self._selection.get_scans(),
1106               'i': self._selection.get_ifs(),
1107               'p': self._selection.get_pols(),
1108               't': self._selection.get_cycles(),
1109               'r': False}#, '_r': 1}
1110        n =  d2[self._panelling] or d1[self._panelling]()
1111        nstack = d2[self._stacking] or d1[self._stacking]()
1112        # handle row panelling/stacking
1113        if self._panelling == 'r':
1114            nstack = 1
1115        elif self._stacking == 'r':
1116            n = 1
1117        return n,nstack
1118
1119    def _get_label(self, scan, row, mode, userlabel=None):
1120        if isinstance(userlabel, list) and len(userlabel) == 0:
1121            userlabel = " "
1122        pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
1123        if len(pms):
1124            poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
1125        else:
1126            poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
1127        d = {'b': "Beam "+str(scan.getbeam(row)),
1128             #'s': scan._getsourcename(row),
1129             's': "Scan "+str(scan.getscan(row))+\
1130                  " ("+str(scan._getsourcename(row))+")",
1131             'i': "IF"+str(scan.getif(row)),
1132             'p': poleval,
1133             't': str(scan.get_time(row)),
1134             'r': "row "+str(row),
1135             #'_r': str(scan.get_time(row))+",\nIF"+str(scan.getif(row))+", "+poleval+", Beam"+str(scan.getbeam(row)) }
1136             '_r': "" }
1137        return userlabel or d[mode]
1138
1139    def plotazel(self, scan=None, outfile=None):
1140        """
1141        plot azimuth and elevation versus time of a scantable
1142        """
1143        visible = rcParams['plotter.gui']
1144        from matplotlib import pylab as PL
1145        from matplotlib.dates import DateFormatter, timezone
1146        from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
1147        from matplotlib.ticker import MultipleLocator
1148        from numpy import array, pi
1149        if not visible or not self._visible:
1150            PL.ioff()
1151            from matplotlib.backends.backend_agg import FigureCanvasAgg
1152            PL.gcf().canvas.switch_backends(FigureCanvasAgg)
1153        self._data = scan
1154        self._outfile = outfile
1155        dates = self._data.get_time(asdatetime=True)
1156        t = PL.date2num(dates)
1157        tz = timezone('UTC')
1158        PL.cla()
1159        PL.ioff()
1160        PL.clf()
1161        # Adjust subplot margins
1162        if len(self._margins) != 6:
1163            self.set_margin(refresh=False)
1164        lef, bot, rig, top, wsp, hsp = self._margins
1165        PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1166                                 wspace=wsp,hspace=hsp)
1167
1168        tdel = max(t) - min(t)
1169        ax = PL.subplot(2,1,1)
1170        el = array(self._data.get_elevation())*180./pi
1171        PL.ylabel('El [deg.]')
1172        dstr = dates[0].strftime('%Y/%m/%d')
1173        if tdel > 1.0:
1174            dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
1175            dstr = dstr + " - " + dstr2
1176            majloc = DayLocator()
1177            minloc = HourLocator(range(0,23,12))
1178            timefmt = DateFormatter("%b%d")
1179        elif tdel > 24./60.:
1180            timefmt = DateFormatter('%H:%M')
1181            majloc = HourLocator()
1182            minloc = MinuteLocator(30)
1183        else:
1184            timefmt = DateFormatter('%H:%M')
1185            majloc = MinuteLocator(interval=5)
1186            minloc = SecondLocator(30)
1187
1188        PL.title(dstr)
1189        if tdel == 0.0:
1190            th = (t - PL.floor(t))*24.0
1191            PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
1192        else:
1193            PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
1194            #ax.grid(True)
1195            ax.xaxis.set_major_formatter(timefmt)
1196            ax.xaxis.set_major_locator(majloc)
1197            ax.xaxis.set_minor_locator(minloc)
1198        ax.yaxis.grid(True)
1199        yloc = MultipleLocator(30)
1200        ax.set_ylim(0,90)
1201        ax.yaxis.set_major_locator(yloc)
1202        if tdel > 1.0:
1203            labels = ax.get_xticklabels()
1204        #    PL.setp(labels, fontsize=10, rotation=45)
1205            PL.setp(labels, fontsize=10)
1206
1207        # Az plot
1208        az = array(self._data.get_azimuth())*180./pi
1209        if min(az) < 0:
1210            for irow in range(len(az)):
1211                if az[irow] < 0: az[irow] += 360.0
1212
1213        ax2 = PL.subplot(2,1,2)
1214        #PL.xlabel('Time (UT [hour])')
1215        PL.ylabel('Az [deg.]')
1216        if tdel == 0.0:
1217            PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
1218        else:
1219            PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
1220            ax2.xaxis.set_major_formatter(timefmt)
1221            ax2.xaxis.set_major_locator(majloc)
1222            ax2.xaxis.set_minor_locator(minloc)
1223        #ax2.grid(True)
1224        ax2.set_ylim(0,360)
1225        ax2.yaxis.grid(True)
1226        #hfmt = DateFormatter('%H')
1227        #hloc = HourLocator()
1228        yloc = MultipleLocator(60)
1229        ax2.yaxis.set_major_locator(yloc)
1230        if tdel > 1.0:
1231            labels = ax2.get_xticklabels()
1232            PL.setp(labels, fontsize=10)
1233            PL.xlabel('Time (UT [day])')
1234        else:
1235            PL.xlabel('Time (UT [hour])')
1236
1237        PL.ion()
1238        PL.draw()
1239        if (self._outfile is not None):
1240           PL.savefig(self._outfile)
1241
1242    def plotpointing(self, scan=None, outfile=None):
1243        """
1244        plot telescope pointings
1245        """
1246        visible = rcParams['plotter.gui']
1247        from matplotlib import pylab as PL
1248        from numpy import array, pi
1249        if not visible or not self._visible:
1250            PL.ioff()
1251            from matplotlib.backends.backend_agg import FigureCanvasAgg
1252            PL.gcf().canvas.switch_backends(FigureCanvasAgg)
1253        self._data = scan
1254        self._outfile = outfile
1255        dir = array(self._data.get_directionval()).transpose()
1256        ra = dir[0]*180./pi
1257        dec = dir[1]*180./pi
1258        PL.cla()
1259        #PL.ioff()
1260        PL.clf()
1261        # Adjust subplot margins
1262        if len(self._margins) != 6:
1263            self.set_margin(refresh=False)
1264        lef, bot, rig, top, wsp, hsp = self._margins
1265        PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1266                                 wspace=wsp,hspace=hsp)
1267        ax = PL.gca()
1268        #ax = PL.axes([0.1,0.1,0.8,0.8])
1269        #ax = PL.axes([0.1,0.1,0.8,0.8])
1270        ax.set_aspect('equal')
1271        PL.plot(ra, dec, 'b,')
1272        PL.xlabel('RA [deg.]')
1273        PL.ylabel('Declination [deg.]')
1274        PL.title('Telescope pointings')
1275        [xmin,xmax,ymin,ymax] = PL.axis()
1276        PL.axis([xmax,xmin,ymin,ymax])
1277        #PL.ion()
1278        PL.draw()
1279        if (self._outfile is not None):
1280           PL.savefig(self._outfile)
1281
1282    # plot total power data
1283    # plotting in time is not yet implemented..
1284    @asaplog_post_dec
1285    def plottp(self, scan=None, outfile=None):
1286        if self._plotter.is_dead:
1287            if hasattr(self._plotter.figmgr,'casabar'):
1288                del self._plotter.figmgr.casabar
1289            self._plotter = self._newplotter()
1290            self._plotter.figmgr.casabar=self._newcasabar()
1291        self._plotter.hold()
1292        self._plotter.clear()
1293        from asap import scantable
1294        if not self._data and not scan:
1295            msg = "Input is not a scantable"
1296            raise TypeError(msg)
1297        if isinstance(scan, scantable):
1298            if self._data is not None:
1299                if scan != self._data:
1300                    self._data = scan
1301                    # reset
1302                    self._reset()
1303            else:
1304                self._data = scan
1305                self._reset()
1306        # ranges become invalid when abcissa changes?
1307        #if self._abcunit and self._abcunit != self._data.get_unit():
1308        #    self._minmaxx = None
1309        #    self._minmaxy = None
1310        #    self._abcunit = self._data.get_unit()
1311        #    self._datamask = None
1312
1313        # Adjust subplot margins
1314        if len(self._margins) !=6: self.set_margin(refresh=False)
1315        lef, bot, rig, top, wsp, hsp = self._margins
1316        self._plotter.figure.subplots_adjust(
1317            left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
1318        if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.disable_button()
1319        self._plottp(self._data)
1320        if self._minmaxy is not None:
1321            self._plotter.set_limits(ylim=self._minmaxy)
1322        self._plotter.release()
1323        self._plotter.tidy()
1324        self._plotter.show(hardrefresh=False)
1325        return
1326
1327    def _plottp(self,scan):
1328        """
1329        private method for plotting total power data
1330        """
1331        from numpy import ma, array, arange, logical_not
1332        r=0
1333        nr = scan.nrow()
1334        a0,b0 = -1,-1
1335        allxlim = []
1336        allylim = []
1337        y=[]
1338        self._plotter.set_panels()
1339        self._plotter.palette(0)
1340        #title
1341        #xlab = self._abcissa and self._abcissa[panelcount] \
1342        #       or scan._getabcissalabel()
1343        #ylab = self._ordinate and self._ordinate[panelcount] \
1344        #       or scan._get_ordinate_label()
1345        xlab = self._abcissa or 'row number' #or Time
1346        ylab = self._ordinate or scan._get_ordinate_label()
1347        self._plotter.set_axes('xlabel',xlab)
1348        self._plotter.set_axes('ylabel',ylab)
1349        lbl = self._get_label(scan, r, 's', self._title)
1350        if isinstance(lbl, list) or isinstance(lbl, tuple):
1351        #    if 0 <= panelcount < len(lbl):
1352        #        lbl = lbl[panelcount]
1353        #    else:
1354                # get default label
1355             lbl = self._get_label(scan, r, self._panelling, None)
1356        self._plotter.set_axes('title',lbl)
1357        y=array(scan._get_column(scan._getspectrum,-1))
1358        m = array(scan._get_column(scan._getmask,-1))
1359        y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1360        x = arange(len(y))
1361        # try to handle spectral data somewhat...
1362        l,m = y.shape
1363        if m > 1:
1364            y=y.mean(axis=1)
1365        plotit = self._plotter.plot
1366        llbl = self._get_label(scan, r, self._stacking, None)
1367        self._plotter.set_line(label=llbl)
1368        if len(x) > 0:
1369            plotit(x,y)
1370
1371
1372    # forwards to matplotlib.Figure.text
1373    def figtext(self, *args, **kwargs):
1374        """
1375        Add text to figure at location x,y (relative 0-1 coords).
1376        This method forwards *args and **kwargs to a Matplotlib method,
1377        matplotlib.Figure.text.
1378        See the method help for detailed information.
1379        """
1380        self._plotter.text(*args, **kwargs)
1381    # end matplotlib.Figure.text forwarding function
1382
1383
1384    # printing header information
1385    @asaplog_post_dec
1386    def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
1387        """
1388        print data (scantable) header on the plot and/or logger.
1389        To plot the header on the plot, this method should be called after
1390        plotting spectra by the method, asapplotter.plot.
1391        Parameters:
1392            plot:      whether or not print header info on the plot.
1393            fontsize:  header font size (valid only plot=True)
1394            logger:    whether or not print header info on the logger.
1395            selstr:    additional selection string (not verified)
1396            extrastr:  additional string to print at the beginning (not verified)
1397        """
1398        if not plot and not logger:
1399            return
1400        if not self._data:
1401            raise RuntimeError("No scantable has been set yet.")
1402        # Now header will be printed on plot and/or logger.
1403        # Get header information and format it.
1404        ssum=self._data._list_header()
1405        # Print Observation header to the upper-left corner of plot
1406        headstr=[ssum[ssum.find('Observer:'):ssum.find('Flux Unit:')]]
1407        headstr.append(ssum[ssum.find('Beams:'):ssum.find('Observer:')]
1408                       +ssum[ssum.find('Rest Freqs:'):ssum.find('Abcissa:')])
1409        if extrastr != '':
1410            headstr[0]=extrastr+'\n'+headstr[0]
1411            self._headtext['extrastr'] = extrastr
1412        if selstr != '':
1413            selstr += '\n'
1414            self._headtext['selstr'] = selstr
1415        ssel=(selstr+self._data.get_selection().__str__()+self._selection.__str__() or 'none')
1416        headstr.append('***Selections***\n'+ssel)
1417
1418        if plot:
1419            self._plotter.hold()
1420            self._header_plot(headstr,fontsize=fontsize)
1421            import time
1422            self._plotter.figure.text(0.99,0.01,
1423                            time.strftime("%a %d %b %Y  %H:%M:%S %Z"),
1424                            horizontalalignment='right',
1425                            verticalalignment='bottom',fontsize=8)
1426            self._plotter.release()
1427        if logger:
1428            selstr = "Selections:    "+ssel
1429            asaplog.push("----------------\n  Plot Summary\n----------------")
1430            asaplog.push(extrastr)
1431            asaplog.push(ssum[ssum.find('Beams:'):ssum.find('Selection:')]\
1432                         #+ selstr + ssum[ssum.find('Scan Source'):])
1433                         + selstr)
1434        self._headtext['string'] = headstr
1435        del ssel, ssum, headstr
1436
1437    def _header_plot(self, texts, fontsize=9):
1438        self._headtext['textobj']=[]
1439        nstcol=len(texts)
1440        for i in range(nstcol):
1441            self._headtext['textobj'].append(
1442                self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1443                                          texts[i],
1444                                          horizontalalignment='left',
1445                                          verticalalignment='top',
1446                                          fontsize=fontsize))
1447
1448    def clear_header(self):
1449        if not self._headtext['textobj']:
1450            asaplog.push("No header has been plotted. Exit without any operation")
1451            asaplog.post("WARN")
1452        else:
1453            self._plotter.hold()
1454            for textobj in self._headtext['textobj']:
1455                #if textobj.get_text() in self._headstring:
1456                try:
1457                    textobj.remove()
1458                except NotImplementedError:
1459                    self._plotter.figure.texts.pop(self._plotter.figure.texts.index(textobj))
1460            self._plotter.release()
1461        self._reset_header()
Note: See TracBrowser for help on using the repository browser.