source: trunk/python/asapplotter.py @ 2155

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

New Development: Yes

JIRA Issue: No (reorganization of modules)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: the modules casatoolbar, flagtoolbar, notaionwindow, were reorganized as customgui_base, customgui_tkagg, and customgui_qt4agg, based on backend

Test Programs: List test programs

Put in Release Notes: Yes/No?

Module(s): Module Names change impacts.

Description: Describe your changes here...


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