source: trunk/python/asapplotter.py @ 2538

Last change on this file since 2538 was 2538, checked in by Kana Sugimoto, 12 years ago

New Development: No

JIRA Issue: Yes (related to CAS-3749)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: added a new method, _alive(), in asaplotbase class

Test Programs: Interactive tests

Put in Release Notes: No

Module(s):

Description:

Added a new method, _alive(), in asaplotbase class. The method return True
if asaplot instance is alive. More complete check compared to 'is_dead' parameter.
asapplotter._assert_plotter method is simplified by calling this function.

Fixed misc bugs found in interactive tests.

  1. set back linewidth = 1 in plots invoked by modules, interactivemask, asapmath, and asapfitter.
  2. interactivemask module: plotter in properly quited at the end.
  3. interactivemask module: avoid error when user close interacive plot window before calling the finalization method, finish_selection().
  4. added definition of a dummy function, quit(), in asaplot class.


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