source: trunk/python/asapplotter.py @ 2277

Last change on this file since 2277 was 2277, checked in by Malte Marquarding, 13 years ago

revert accidental changes

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