source: trunk/python/asapplotter.py @ 2608

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

New Development: No

JIRA Issue: No (removal of debug comments)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): asapplotter, flagplotter, sdplot, and sdflag

Description:

Removed debug comments and destructor of asapplotter.


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.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
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) and (scan != self._data):
415                del self._data
416                msg = "A new scantable is set to the plotter. "\
417                      "The masks and data selections are reset."
418                asaplog.push( msg )
419            self._data = scan
420            # reset
421            self._reset()
422        else:
423            msg = "Input is not a scantable"
424            raise TypeError(msg)
425
426        # ranges become invalid when unit changes
427        if self._abcunit and self._abcunit != self._data.get_unit():
428            self._minmaxx = None
429            self._minmaxy = None
430            self._abcunit = self._data.get_unit()
431            self._datamask = None
432        if refresh: self.plot()
433
434    @asaplog_post_dec
435    def set_mode(self, stacking=None, panelling=None, refresh=True):
436        """
437        Set the plots look and feel, i.e. what you want to see on the plot.
438        Parameters:
439            stacking:     tell the plotter which variable to plot
440                          as line colour overlays (default 'pol')
441            panelling:    tell the plotter which variable to plot
442                          across multiple panels (default 'scan'
443            refresh:      True (default) or False. If True, the plot is
444                          replotted based on the new parameter setting(s).
445                          Otherwise,the parameter(s) are set without replotting.
446        Note:
447            Valid modes are:
448                 'beam' 'Beam' 'b':     Beams
449                 'if' 'IF' 'i':         IFs
450                 'pol' 'Pol' 'p':       Polarisations
451                 'scan' 'Scan' 's':     Scans
452                 'time' 'Time' 't':     Times
453                 'row' 'Row' 'r':       Rows
454            When either 'stacking' or 'panelling' is set to 'row',
455            the other parameter setting is ignored.
456        """
457        msg = "Invalid mode"
458        if not self.set_panelling(panelling) or \
459               not self.set_stacking(stacking):
460            raise TypeError(msg)
461        #if self._panelling == 'r':
462        #    self._stacking = '_r'
463        #if self._stacking == 'r':
464        #    self._panelling = '_r'
465        if refresh and self._data: self.plot(self._data)
466        return
467
468    def set_panelling(self, what=None):
469        """Set the 'panelling' mode i.e. which type of spectra should be
470        spread across different panels.
471        """
472
473        mode = what
474        if mode is None:
475             mode = rcParams['plotter.panelling']
476        md = self._translate(mode)
477        if md:
478            self._panelling = md
479            self._title = None
480            #if md == 'r':
481            #    self._stacking = '_r'
482            # you need to reset counters for multi page plotting
483            self._reset_counters()
484            return True
485        return False
486
487    def set_layout(self,rows=None,cols=None,refresh=True):
488        """
489        Set the multi-panel layout, i.e. how many rows and columns plots
490        are visible.
491        Parameters:
492             rows:   The number of rows of plots
493             cols:   The number of columns of plots
494             refresh:  True (default) or False. If True, the plot is
495                       replotted based on the new parameter setting(s).
496                       Otherwise,the parameter(s) are set without replotting.
497        Note:
498             If no argument is given, the potter reverts to its auto-plot
499             behaviour.
500        """
501        self._rows = rows
502        self._cols = cols
503        if refresh and self._data: self.plot(self._data)
504        return
505
506    def set_stacking(self, what=None):
507        """Set the 'stacking' mode i.e. which type of spectra should be
508        overlayed.
509        """
510        mode = what
511        if mode is None:
512             mode = rcParams['plotter.stacking']
513        md = self._translate(mode)
514        if md:
515            self._stacking = md
516            self._lmap = None
517            #if md == 'r':
518            #    self._panelling = '_r'
519            # you need to reset counters for multi page plotting
520            self._reset_counters()
521            return True
522        return False
523
524    def _reset_counters(self):
525        self._startrow = 0
526        self._ipanel = -1
527        self._panelrows = []
528
529    def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True, offset=None):
530        """
531        Set the range of interest on the abcissa of the plot
532        Parameters:
533            [x,y]start,[x,y]end:  The start and end points of the 'zoom' window
534            refresh:  True (default) or False. If True, the plot is
535                      replotted based on the new parameter setting(s).
536                      Otherwise,the parameter(s) are set without replotting.
537            offset:   shift the abcissa by the given amount. The abcissa label will
538                      have '(relative)' appended to it.
539        Note:
540            These become non-sensical when the unit changes.
541            use plotter.set_range() without parameters to reset
542
543        """
544        self._offset = offset
545        if xstart is None and xend is None:
546            self._minmaxx = None
547        else:
548            self._minmaxx = [xstart,xend]
549        if ystart is None and yend is None:
550            self._minmaxy = None
551        else:
552            self._minmaxy = [ystart,yend]
553        if refresh and self._data: self.plot(self._data)
554        return
555
556    def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
557        """
558        Specify a mapping for the legend instead of using the default
559        indices:
560        Parameters:
561            mp:        a list of 'strings'. This should have the same length
562                       as the number of elements on the legend and then maps
563                       to the indeces in order. It is possible to uses latex
564                       math expression. These have to be enclosed in r'',
565                       e.g. r'$x^{2}$'
566            fontsize:  The font size of the label (default None)
567            mode:      where to display the legend
568                       Any other value for loc else disables the legend:
569                        0: auto
570                        1: upper right
571                        2: upper left
572                        3: lower left
573                        4: lower right
574                        5: right
575                        6: center left
576                        7: center right
577                        8: lower center
578                        9: upper center
579                        10: center
580            refresh:    True (default) or False. If True, the plot is
581                        replotted based on the new parameter setting(s).
582                        Otherwise,the parameter(s) are set without replotting.
583
584        Example:
585             If the data has two IFs/rest frequencies with index 0 and 1
586             for CO and SiO:
587             plotter.set_stacking('i')
588             plotter.set_legend(['CO','SiO'])
589             plotter.plot()
590             plotter.set_legend([r'$^{12}CO$', r'SiO'])
591        """
592        self._lmap = mp
593        #self._plotter.legend(mode)
594        self._legendloc = mode
595        if isinstance(fontsize, int):
596            from matplotlib import rc as rcp
597            rcp('legend', fontsize=fontsize)
598        if refresh and self._data: self.plot(self._data)
599        return
600
601    def set_title(self, title=None, fontsize=None, refresh=True):
602        """
603        Set the title of sub-plots. If multiple sub-plots are plotted,
604        multiple titles have to be specified.
605        Parameters:
606            title:      a list of titles of sub-plots.
607            fontsize:   a font size of titles (integer)
608            refresh:    True (default) or False. If True, the plot is
609                        replotted based on the new parameter setting(s).
610                        Otherwise,the parameter(s) are set without replotting.
611        Example:
612             # two panels are visible on the plotter
613             plotter.set_title(['First Panel','Second Panel'])
614        """
615        self._title = title
616        if isinstance(fontsize, int):
617            from matplotlib import rc as rcp
618            rcp('axes', titlesize=fontsize)
619        if refresh and self._data: self.plot(self._data)
620        return
621
622    def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
623        """
624        Set the y-axis label of the plot. If multiple panels are plotted,
625        multiple labels have to be specified.
626        Parameters:
627            ordinate:    a list of ordinate labels. None (default) let
628                         data determine the labels
629            fontsize:    a font size of vertical axis labels (integer)
630            refresh:     True (default) or False. If True, the plot is
631                         replotted based on the new parameter setting(s).
632                         Otherwise,the parameter(s) are set without replotting.
633        Example:
634             # two panels are visible on the plotter
635             plotter.set_ordinate(['First Y-Axis','Second Y-Axis'])
636        """
637        self._ordinate = ordinate
638        if isinstance(fontsize, int):
639            from matplotlib import rc as rcp
640            rcp('axes', labelsize=fontsize)
641            rcp('ytick', labelsize=fontsize)
642        if refresh and self._data: self.plot(self._data)
643        return
644
645    def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
646        """
647        Set the x-axis label of the plot. If multiple panels are plotted,
648        multiple labels have to be specified.
649        Parameters:
650            abcissa:     a list of abcissa labels. None (default) let
651                         data determine the labels
652            fontsize:    a font size of horizontal axis labels (integer)
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        Example:
657             # two panels are visible on the plotter
658             plotter.set_ordinate(['First X-Axis','Second X-Axis'])
659        """
660        self._abcissa = abcissa
661        if isinstance(fontsize, int):
662            from matplotlib import rc as rcp
663            rcp('axes', labelsize=fontsize)
664            rcp('xtick', labelsize=fontsize)
665        if refresh and self._data: self.plot(self._data)
666        return
667
668    def set_colors(self, colmap, refresh=True):
669        """
670        Set the colours to be used. The plotter will cycle through
671        these colours when lines are overlaid (stacking mode).
672        Parameters:
673            colmap:     a list of colour names
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        Example:
678             plotter.set_colors('red green blue')
679             # If for example four lines are overlaid e.g I Q U V
680             # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
681             # and 'V' will be 'red' again.
682        """
683        #if isinstance(colmap,str):
684        #    colmap = colmap.split()
685        #self._plotter.palette(0, colormap=colmap)
686        self._colormap = colmap
687        if refresh and self._data: self.plot(self._data)
688
689    # alias for english speakers
690    set_colours = set_colors
691
692    def set_histogram(self, hist=True, linewidth=None, refresh=True):
693        """
694        Enable/Disable histogram-like plotting.
695        Parameters:
696            hist:        True (default) or False. The fisrt default
697                         is taken from the .asaprc setting
698                         plotter.histogram
699            linewidth:   a line width
700            refresh:     True (default) or False. If True, the plot is
701                         replotted based on the new parameter setting(s).
702                         Otherwise,the parameter(s) are set without replotting.
703        """
704        self._hist = hist
705        if isinstance(linewidth, float) or isinstance(linewidth, int):
706            from matplotlib import rc as rcp
707            rcp('lines', linewidth=linewidth)
708        if refresh and self._data: self.plot(self._data)
709
710    def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
711        """
712        Set the linestyles to be used. The plotter will cycle through
713        these linestyles when lines are overlaid (stacking mode) AND
714        only one color has been set.
715        Parameters:
716            linestyles:      a list of linestyles to use.
717                             'line', 'dashed', 'dotted', 'dashdot',
718                             'dashdotdot' and 'dashdashdot' are
719                             possible
720            linewidth:       a line width
721            refresh:         True (default) or False. If True, the plot is
722                             replotted based on the new parameter setting(s).
723                             Otherwise,the parameter(s) are set without replotting.
724        Example:
725             plotter.set_colors('black')
726             plotter.set_linestyles('line dashed dotted dashdot')
727             # If for example four lines are overlaid e.g I Q U V
728             # 'I' will be 'solid', 'Q' will be 'dashed',
729             # U will be 'dotted' and 'V' will be 'dashdot'.
730        """
731        #if isinstance(linestyles,str):
732        #    linestyles = linestyles.split()
733        #self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
734        self._linestyles = linestyles
735        if isinstance(linewidth, float) or isinstance(linewidth, int):
736            from matplotlib import rc as rcp
737            rcp('lines', linewidth=linewidth)
738        if refresh and self._data: self.plot(self._data)
739
740    def set_font(self, refresh=True,**kwargs):
741        """
742        Set font properties.
743        Parameters:
744            family:    one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
745            style:     one of 'normal' (or 'roman'), 'italic'  or 'oblique'
746            weight:    one of 'normal or 'bold'
747            size:      the 'general' font size, individual elements can be adjusted
748                       seperately
749            refresh:   True (default) or False. If True, the plot is
750                       replotted based on the new parameter setting(s).
751                       Otherwise,the parameter(s) are set without replotting.
752        """
753        from matplotlib import rc as rcp
754        fdict = {}
755        for k,v in kwargs.iteritems():
756            if v:
757                fdict[k] = v
758        self._fp = FontProperties(**fdict)
759        if refresh and self._data: self.plot(self._data)
760
761    def set_margin(self,margin=[],refresh=True):
762        """
763        Set margins between subplots and plot edges.
764        Parameters:
765            margin:   a list of margins in figure coordinate (0-1),
766                      i.e., fraction of the figure width or height.
767                      The order of elements should be:
768                      [left, bottom, right, top, horizontal space btw panels,
769                      vertical space btw panels].
770            refresh:  True (default) or False. If True, the plot is
771                      replotted based on the new parameter setting(s).
772                      Otherwise,the parameter(s) are set without replotting.
773        Note
774        * When margin is not specified, the values are reset to the defaults
775          of matplotlib.
776        * If any element is set to be None, the current value is adopted.
777        """
778        if margin == []: self._margins=self._reset_margin()
779        else:
780            self._margins=[None]*6
781            self._margins[0:len(margin)]=margin
782        #print "panel margin set to ",self._margins
783        if refresh and self._data: self.plot(self._data)
784
785    def _reset_margin(self):
786        ks=map(lambda x: 'figure.subplot.'+x,
787               ['left','bottom','right','top','hspace','wspace'])
788        return map(matplotlib.rcParams.get,ks)
789
790    def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
791                   location=None):
792        """
793        Plot a line catalog.
794        Parameters:
795            linecat:      the linecatalog to plot
796            doppler:      the velocity shift to apply to the frequencies
797            deltachan:    the number of channels to include each side of the
798                          line to determine a local maximum/minimum
799            rotate:       the rotation (in degrees) for the text label (default 90.0)
800            location:     the location of the line annotation from the 'top',
801                          'bottom' or alternate (None - the default)
802        Notes:
803        If the spectrum is flagged no line will be drawn in that location.
804        """
805        errmsg = "Cannot plot spectral lines. Need to plot scantable first."
806        self._assert_plotter(action="halt",errmsg=errmsg)
807        if not self._data:
808            raise RuntimeError("No scantable has been plotted yet.")
809        from asap._asap import linecatalog
810        if not isinstance(linecat, linecatalog):
811            raise ValueError("'linecat' isn't of type linecatalog.")
812        if not self._data.get_unit().endswith("Hz"):
813            raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
814        from numpy import ma
815        for j in range(len(self._plotter.subplots)):
816            self._plotter.subplot(j)
817            lims = self._plotter.axes.get_xlim()
818            for row in range(linecat.nrow()):
819                # get_frequency returns MHz
820                base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
821                restf = linecat.get_frequency(row)/base[self._data.get_unit()]
822                c = 299792.458
823                freq = restf*(1.0-doppler/c)
824                if lims[0] < freq < lims[1]:
825                    if location is None:
826                        loc = 'bottom'
827                        if row%2: loc='top'
828                    else: loc = location
829                    maxys = []
830                    for line in self._plotter.axes.lines:
831                        v = line._x
832                        asc = v[0] < v[-1]
833
834                        idx = None
835                        if not asc:
836                            if v[len(v)-1] <= freq <= v[0]:
837                                i = len(v)-1
838                                while i>=0 and v[i] < freq:
839                                    idx = i
840                                    i-=1
841                        else:
842                           if v[0] <= freq <= v[len(v)-1]:
843                                i = 0
844                                while  i<len(v) and v[i] < freq:
845                                    idx = i
846                                    i+=1
847                        if idx is not None:
848                            lower = idx - deltachan
849                            upper = idx + deltachan
850                            if lower < 0: lower = 0
851                            if upper > len(v): upper = len(v)
852                            s = slice(lower, upper)
853                            y = line._y[s]
854                            maxy = ma.maximum(y)
855                            if isinstance( maxy, float):
856                                maxys.append(maxy)
857                    if len(maxys):
858                        peak = max(maxys)
859                        if peak > self._plotter.axes.get_ylim()[1]:
860                            loc = 'bottom'
861                    else:
862                        continue
863                    self._plotter.vline_with_label(freq, peak,
864                                                   linecat.get_name(row),
865                                                   location=loc, rotate=rotate)
866        self._plotter.show(hardrefresh=False)
867
868
869    def save(self, filename=None, orientation=None, dpi=None):
870        """
871        Save the plot to a file. The known formats are 'png', 'ps', 'eps'.
872        Parameters:
873             filename:    The name of the output file. This is optional
874                          and autodetects the image format from the file
875                          suffix. If non filename is specified a file
876                          called 'yyyymmdd_hhmmss.png' is created in the
877                          current directory.
878             orientation: optional parameter for postscript only (not eps).
879                          'landscape', 'portrait' or None (default) are valid.
880                          If None is choosen for 'ps' output, the plot is
881                          automatically oriented to fill the page.
882             dpi:         The dpi of the output non-ps plot
883        """
884        errmsg = "Cannot save figure. Need to plot first."
885        self._assert_plotter(action="halt",errmsg=errmsg)
886       
887        self._plotter.save(filename,orientation,dpi)
888        return
889
890    @asaplog_post_dec
891    def set_mask(self, mask=None, selection=None, refresh=True):
892        """
893        Set a plotting mask for a specific polarization.
894        This is useful for masking out 'noise' Pangle outside a source.
895        Parameters:
896             mask:           a mask from scantable.create_mask
897             selection:      the spectra to apply the mask to.
898             refresh:        True (default) or False. If True, the plot is
899                             replotted based on the new parameter setting(s).
900                             Otherwise,the parameter(s) are set without replotting.
901        Example:
902             select = selector()
903             select.setpolstrings('Pangle')
904             plotter.set_mask(mymask, select)
905        """
906        if not self._data:
907            msg = "Can only set mask after a first call to plot()"
908            raise RuntimeError(msg)
909        if len(mask):
910            if isinstance(mask, list) or isinstance(mask, tuple):
911                self._usermask = array(mask)
912            else:
913                self._usermask = mask
914        if mask is None and selection is None:
915            self._usermask = []
916            self._maskselection = None
917        if isinstance(selection, selector):
918            self._maskselection = {'b': selection.get_beams(),
919                                   's': selection.get_scans(),
920                                   'i': selection.get_ifs(),
921                                   'p': selection.get_pols(),
922                                   't': [] }
923        else:
924            self._maskselection = None
925        if refresh: self.plot(self._data)
926
927    def _slice_indeces(self, data):
928        mn = self._minmaxx[0]
929        mx = self._minmaxx[1]
930        asc = data[0] < data[-1]
931        start=0
932        end = len(data)-1
933        inc = 1
934        if not asc:
935            start = len(data)-1
936            end = 0
937            inc = -1
938        # find min index
939        #while start > 0 and data[start] < mn:
940        #    start+= inc
941        minind=start
942        for ind in xrange(start,end+inc,inc):
943            if data[ind] > mn: break
944            minind=ind
945        # find max index
946        #while end > 0 and data[end] > mx:
947        #    end-=inc
948        #if end > 0: end +=1
949        maxind=end
950        for ind in xrange(end,start-inc,-inc):
951            if data[ind] < mx: break
952            maxind=ind
953        start=minind
954        end=maxind
955        if start > end:
956            return end,start+1
957        elif start < end:
958            return start,end+1
959        else:
960            return start,end
961
962    def _reset(self):
963        self._usermask = []
964        self._usermaskspectra = None
965        self._offset = None
966        self.set_selection(None, False)
967        self._reset_header()
968
969    def _reset_header(self):
970        self._headtext={'string': None, 'textobj': None}
971
972    def _plot(self, scan):
973        savesel = scan.get_selection()
974        sel = savesel +  self._selection
975        order = self._get_sortstring([self._panelling,self._stacking])
976        if order:
977            sel.set_order(order)
978        scan.set_selection(sel)
979        d = {'b': scan.getbeam, 's': scan.getscan,
980             'i': scan.getif, 'p': scan.getpol, 't': scan.get_time,
981             'r': int}#, '_r': int}
982
983        polmodes = dict(zip(self._selection.get_pols(),
984                            self._selection.get_poltypes()))
985        # this returns either a tuple of numbers or a length  (ncycles)
986        # convert this into lengths
987        n0,nstack0 = self._get_selected_n(scan)
988        if isinstance(n0, int): n = n0
989        else: n = len(n0)
990        if isinstance(nstack0, int): nstack = nstack0
991        else: nstack = len(nstack0)
992        # In case of row stacking
993        rowstack = False
994        titlemode = self._panelling
995        if self._stacking == "r" and self._panelling != "r":
996            rowstack = True
997            titlemode = '_r'
998        nptot = n
999        maxpanel, maxstack = 16,16
1000        if nstack > maxstack:
1001            msg ="Scan to be overlayed contains more than %d selections.\n" \
1002                  "Selecting first %d selections..." % (maxstack, maxstack)
1003            asaplog.push(msg)
1004            asaplog.post('WARN')
1005            nstack = min(nstack,maxstack)
1006        #n = min(n-self._ipanel-1,maxpanel)
1007        n = n-self._ipanel-1
1008
1009        ganged = False
1010        if n > 1:
1011            ganged = rcParams['plotter.ganged']
1012            if self._panelling == 'i':
1013                ganged = False
1014            if self._rows and self._cols:
1015                n = min(n,self._rows*self._cols)
1016                self._plotter.set_panels(rows=self._rows,cols=self._cols,
1017                                         nplots=n,margin=self._margins,ganged=ganged)
1018            else:
1019                n = min(n,maxpanel)
1020                self._plotter.set_panels(rows=n,cols=0,nplots=n,margin=self._margins,ganged=ganged)
1021        else:
1022            self._plotter.set_panels(margin=self._margins)
1023        #r = 0
1024        r = self._startrow
1025        nr = scan.nrow()
1026        a0,b0 = -1,-1
1027        allxlim = []
1028        allylim = []
1029        #newpanel=True
1030        newpanel=False
1031        panelcount,stackcount = 0,0
1032        # If this is not the first page
1033        if r > 0:
1034            # panelling value of the prev page
1035            a0 = d[self._panelling](r-1)
1036            # set the initial stackcount large not to plot
1037            # the start row automatically
1038            stackcount = nstack
1039
1040        while r < nr:
1041            a = d[self._panelling](r)
1042            b = d[self._stacking](r)
1043            if a > a0 and panelcount < n:
1044                if n > 1:
1045                    self._plotter.subplot(panelcount)
1046                self._plotter.palette(0)
1047                #title
1048                xlab = self._abcissa and self._abcissa[panelcount] \
1049                       or scan._getabcissalabel()
1050                if self._offset and not self._abcissa:
1051                    xlab += " (relative)"
1052                ylab = self._ordinate and self._ordinate[panelcount] \
1053                       or scan._get_ordinate_label()
1054                self._plotter.set_axes('xlabel', xlab)
1055                self._plotter.set_axes('ylabel', ylab)
1056                #lbl = self._get_label(scan, r, self._panelling, self._title)
1057                lbl = self._get_label(scan, r, titlemode, self._title)
1058                if isinstance(lbl, list) or isinstance(lbl, tuple):
1059                    if 0 <= panelcount < len(lbl):
1060                        lbl = lbl[panelcount]
1061                    else:
1062                        # get default label
1063                        #lbl = self._get_label(scan, r, self._panelling, None)
1064                        lbl = self._get_label(scan, r, titlemode, None)
1065                self._plotter.set_axes('title',lbl)
1066                newpanel = True
1067                stackcount = 0
1068                panelcount += 1
1069                # save the start row to plot this panel for future revisit.
1070                if self._panelling != 'r' and \
1071                       len(self._panelrows) < self._ipanel+1+panelcount:
1072                    self._panelrows += [r]
1073                   
1074            #if (b > b0 or newpanel) and stackcount < nstack:
1075            if stackcount < nstack and (newpanel or rowstack or (a == a0 and b > b0)):
1076                y = []
1077                if len(polmodes):
1078                    y = scan._getspectrum(r, polmodes[scan.getpol(r)])
1079                else:
1080                    y = scan._getspectrum(r)
1081                # flag application
1082                mr = scan._getflagrow(r)
1083                from numpy import ma, array
1084                if mr:
1085                    y = ma.masked_array(y,mask=mr)
1086                else:
1087                    m = scan._getmask(r)
1088                    from numpy import logical_not, logical_and
1089                    if self._maskselection and len(self._usermask) == len(m):
1090                        if d[self._stacking](r) in self._maskselection[self._stacking]:
1091                            m = logical_and(m, self._usermask)
1092                    y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1093
1094                x = array(scan._getabcissa(r))
1095                if self._offset:
1096                    x += self._offset
1097                if self._minmaxx is not None:
1098                    s,e = self._slice_indeces(x)
1099                    x = x[s:e]
1100                    y = y[s:e]
1101                if len(x) > 1024 and rcParams['plotter.decimate']:
1102                    fac = len(x)/1024
1103                    x = x[::fac]
1104                    y = y[::fac]
1105                llbl = self._get_label(scan, r, self._stacking, self._lmap)
1106                if isinstance(llbl, list) or isinstance(llbl, tuple):
1107                    if 0 <= stackcount < len(llbl):
1108                        # use user label
1109                        llbl = llbl[stackcount]
1110                    else:
1111                        # get default label
1112                        llbl = self._get_label(scan, r, self._stacking, None)
1113                self._plotter.set_line(label=llbl)
1114                plotit = self._plotter.plot
1115                if self._hist: plotit = self._plotter.hist
1116                if len(x) > 0 and not mr:
1117                    plotit(x,y)
1118                    xlim= self._minmaxx or [min(x),max(x)]
1119                    allxlim += xlim
1120                    ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
1121                    allylim += ylim
1122                else:
1123                    xlim = self._minmaxx or []
1124                    allxlim += xlim
1125                    ylim= self._minmaxy or []
1126                    allylim += ylim
1127                stackcount += 1
1128                a0=a
1129                b0=b
1130                # last in colour stack -> autoscale x
1131                if stackcount == nstack and len(allxlim) > 0:
1132                    allxlim.sort()
1133                    self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
1134                    if ganged:
1135                        allxlim = [allxlim[0],allxlim[-1]]
1136                    else:
1137                        # clear
1138                        allxlim =[]
1139
1140            newpanel = False
1141            #a0=a
1142            #b0=b
1143            # ignore following rows
1144            if (panelcount == n and stackcount == nstack) or (r == nr-1):
1145                # last panel -> autoscale y if ganged
1146                #if rcParams['plotter.ganged'] and len(allylim) > 0:
1147                if ganged and len(allylim) > 0:
1148                    allylim.sort()
1149                    self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
1150                break
1151            r+=1 # next row
1152
1153        # save the current counter for multi-page plotting
1154        self._startrow = r+1
1155        self._ipanel += panelcount
1156        if self.casabar_exists():
1157            if self._ipanel >= nptot-1:
1158                self._plotter.figmgr.casabar.disable_next()
1159            else:
1160                self._plotter.figmgr.casabar.enable_next()
1161            if self._ipanel + 1 - panelcount > 0:
1162                self._plotter.figmgr.casabar.enable_prev()
1163            else:
1164                self._plotter.figmgr.casabar.disable_prev()
1165
1166        #reset the selector to the scantable's original
1167        scan.set_selection(savesel)
1168
1169        #temporary switch-off for older matplotlib
1170        #if self._fp is not None:
1171        if self._fp is not None and getattr(self._plotter.figure,'findobj',False):
1172            for o in self._plotter.figure.findobj(Text):
1173                o.set_fontproperties(self._fp)
1174
1175    def _get_sortstring(self, lorders):
1176        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
1177              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME', 'r':None, '_r':None }
1178        if not (type(lorders) == list) and not (type(lorders) == tuple):
1179            return None
1180        if len(lorders) > 0:
1181            lsorts = []
1182            for order in lorders:
1183                if order == "r":
1184                    # don't sort if row panelling/stacking
1185                    return None
1186                ssort = d0[order]
1187                if ssort:
1188                    lsorts.append(ssort)
1189            return lsorts
1190        return None
1191
1192    def set_selection(self, selection=None, refresh=True, **kw):
1193        """
1194        Parameters:
1195            selection:  a selector object (default unset the selection)
1196            refresh:    True (default) or False. If True, the plot is
1197                        replotted based on the new parameter setting(s).
1198                        Otherwise,the parameter(s) are set without replotting.
1199        """
1200        if selection is None:
1201            # reset
1202            if len(kw) == 0:
1203                self._selection = selector()
1204            else:
1205                # try keywords
1206                for k in kw:
1207                    if k not in selector.fields:
1208                        raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
1209                self._selection = selector(**kw)
1210        elif isinstance(selection, selector):
1211            self._selection = selection
1212        else:
1213            raise TypeError("'selection' is not of type selector")
1214
1215        order = self._get_sortstring([self._panelling,self._stacking])
1216        if order:
1217            self._selection.set_order(order)
1218        if refresh and self._data: self.plot(self._data)
1219
1220    def _get_selected_n(self, scan):
1221        d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
1222             'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle,
1223             'r': scan.nrow}#, '_r': False}
1224        d2 = { 'b': self._selection.get_beams(),
1225               's': self._selection.get_scans(),
1226               'i': self._selection.get_ifs(),
1227               'p': self._selection.get_pols(),
1228               't': self._selection.get_cycles(),
1229               'r': False}#, '_r': 1}
1230        n =  d2[self._panelling] or d1[self._panelling]()
1231        nstack = d2[self._stacking] or d1[self._stacking]()
1232        # handle row panelling/stacking
1233        if self._panelling == 'r':
1234            nstack = 1
1235        elif self._stacking == 'r':
1236            n = 1
1237        return n,nstack
1238
1239    def _get_label(self, scan, row, mode, userlabel=None):
1240        if isinstance(userlabel, list) and len(userlabel) == 0:
1241            userlabel = " "
1242        pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
1243        if len(pms):
1244            poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
1245        else:
1246            poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
1247        d = {'b': "Beam "+str(scan.getbeam(row)),
1248             #'s': scan._getsourcename(row),
1249             's': "Scan "+str(scan.getscan(row))+\
1250                  " ("+str(scan._getsourcename(row))+")",
1251             'i': "IF"+str(scan.getif(row)),
1252             'p': poleval,
1253             't': str(scan.get_time(row)),
1254             'r': "row "+str(row),
1255             #'_r': str(scan.get_time(row))+",\nIF"+str(scan.getif(row))+", "+poleval+", Beam"+str(scan.getbeam(row)) }
1256             '_r': "" }
1257        return userlabel or d[mode]
1258
1259    def plotazel(self, scan=None, outfile=None):
1260        """
1261        plot azimuth and elevation versus time of a scantable
1262        """
1263        visible = rcParams['plotter.gui']
1264        from matplotlib import pylab as PL
1265        from matplotlib.dates import DateFormatter
1266        from pytz import timezone
1267        from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
1268        from matplotlib.ticker import MultipleLocator
1269        from numpy import array, pi
1270        if not visible or not self._visible:
1271            PL.ioff()
1272            from matplotlib.backends.backend_agg import FigureCanvasAgg
1273            PL.gcf().canvas.switch_backends(FigureCanvasAgg)
1274        self._data = scan
1275        dates = self._data.get_time(asdatetime=True)
1276        t = PL.date2num(dates)
1277        tz = timezone('UTC')
1278        PL.cla()
1279        PL.ioff()
1280        PL.clf()
1281        # Adjust subplot margins
1282        if not self._margins or 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
1288        tdel = max(t) - min(t)
1289        ax = PL.subplot(2,1,1)
1290        el = array(self._data.get_elevation())*180./pi
1291        PL.ylabel('El [deg.]')
1292        dstr = dates[0].strftime('%Y/%m/%d')
1293        if tdel > 1.0:
1294            dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
1295            dstr = dstr + " - " + dstr2
1296            majloc = DayLocator()
1297            minloc = HourLocator(range(0,23,12))
1298            timefmt = DateFormatter("%b%d")
1299        elif tdel > 24./60.:
1300            timefmt = DateFormatter('%H:%M')
1301            majloc = HourLocator()
1302            minloc = MinuteLocator(30)
1303        else:
1304            timefmt = DateFormatter('%H:%M')
1305            majloc = MinuteLocator(interval=5)
1306            minloc = SecondLocator(30)
1307
1308        PL.title(dstr)
1309        if tdel == 0.0:
1310            th = (t - PL.floor(t))*24.0
1311            PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
1312        else:
1313            PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
1314            #ax.grid(True)
1315            ax.xaxis.set_major_formatter(timefmt)
1316            ax.xaxis.set_major_locator(majloc)
1317            ax.xaxis.set_minor_locator(minloc)
1318        ax.yaxis.grid(True)
1319        yloc = MultipleLocator(30)
1320        ax.set_ylim(0,90)
1321        ax.yaxis.set_major_locator(yloc)
1322        if tdel > 1.0:
1323            labels = ax.get_xticklabels()
1324        #    PL.setp(labels, fontsize=10, rotation=45)
1325            PL.setp(labels, fontsize=10)
1326
1327        # Az plot
1328        az = array(self._data.get_azimuth())*180./pi
1329        if min(az) < 0:
1330            for irow in range(len(az)):
1331                if az[irow] < 0: az[irow] += 360.0
1332
1333        ax2 = PL.subplot(2,1,2)
1334        #PL.xlabel('Time (UT [hour])')
1335        PL.ylabel('Az [deg.]')
1336        if tdel == 0.0:
1337            PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
1338        else:
1339            PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
1340            ax2.xaxis.set_major_formatter(timefmt)
1341            ax2.xaxis.set_major_locator(majloc)
1342            ax2.xaxis.set_minor_locator(minloc)
1343        #ax2.grid(True)
1344        ax2.set_ylim(0,360)
1345        ax2.yaxis.grid(True)
1346        #hfmt = DateFormatter('%H')
1347        #hloc = HourLocator()
1348        yloc = MultipleLocator(60)
1349        ax2.yaxis.set_major_locator(yloc)
1350        if tdel > 1.0:
1351            labels = ax2.get_xticklabels()
1352            PL.setp(labels, fontsize=10)
1353            PL.xlabel('Time (UT [day])')
1354        else:
1355            PL.xlabel('Time (UT [hour])')
1356
1357        PL.ion()
1358        PL.draw()
1359        if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
1360        if (outfile is not None):
1361           PL.savefig(outfile)
1362
1363    def plotpointing(self, scan=None, outfile=None):
1364        """
1365        plot telescope pointings
1366        """
1367        visible = rcParams['plotter.gui']
1368        from matplotlib import pylab as PL
1369        from numpy import array, pi
1370        if not visible or not self._visible:
1371            PL.ioff()
1372            from matplotlib.backends.backend_agg import FigureCanvasAgg
1373            PL.gcf().canvas.switch_backends(FigureCanvasAgg)
1374        self._data = scan
1375        dir = array(self._data.get_directionval()).transpose()
1376        ra = dir[0]*180./pi
1377        dec = dir[1]*180./pi
1378        PL.cla()
1379        #PL.ioff()
1380        PL.clf()
1381        # Adjust subplot margins
1382        if not self._margins or len(self._margins) != 6:
1383            self.set_margin(refresh=False)
1384        lef, bot, rig, top, wsp, hsp = self._margins
1385        PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1386                                 wspace=wsp,hspace=hsp)
1387        ax = PL.gca()
1388        #ax = PL.axes([0.1,0.1,0.8,0.8])
1389        #ax = PL.axes([0.1,0.1,0.8,0.8])
1390        ax.set_aspect('equal')
1391        PL.plot(ra, dec, 'b,')
1392        PL.xlabel('RA [deg.]')
1393        PL.ylabel('Declination [deg.]')
1394        PL.title('Telescope pointings')
1395        [xmin,xmax,ymin,ymax] = PL.axis()
1396        PL.axis([xmax,xmin,ymin,ymax])
1397        PL.ion()
1398        PL.draw()
1399        if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
1400        if (outfile is not None):
1401           PL.savefig(outfile)
1402
1403    # plot total power data
1404    # plotting in time is not yet implemented..
1405    @asaplog_post_dec
1406    def plottp(self, scan=None):
1407        self._assert_plotter(action="reload")
1408        self._plotter.hold()
1409        self._plotter.clear()
1410        from asap import scantable
1411        if not self._data and not scan:
1412            msg = "Input is not a scantable"
1413            raise TypeError(msg)
1414        if isinstance(scan, scantable):
1415            if self._data is not None:
1416                if scan != self._data:
1417                    self._data = scan
1418                    # reset
1419                    self._reset()
1420            else:
1421                self._data = scan
1422                self._reset()
1423        # ranges become invalid when abcissa changes?
1424        #if self._abcunit and self._abcunit != self._data.get_unit():
1425        #    self._minmaxx = None
1426        #    self._minmaxy = None
1427        #    self._abcunit = self._data.get_unit()
1428        #    self._datamask = None
1429
1430        # Adjust subplot margins
1431        if not self._margins or len(self._margins) !=6:
1432            self.set_margin(refresh=False)
1433        lef, bot, rig, top, wsp, hsp = self._margins
1434        self._plotter.figure.subplots_adjust(
1435            left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
1436        if self.casabar_exists(): self._plotter.figmgr.casabar.disable_button()
1437        self._plottp(self._data)
1438        if self._minmaxy is not None:
1439            self._plotter.set_limits(ylim=self._minmaxy)
1440        self._plotter.release()
1441        self._plotter.tidy()
1442        self._plotter.show(hardrefresh=False)
1443        return
1444
1445    def _plottp(self,scan):
1446        """
1447        private method for plotting total power data
1448        """
1449        from numpy import ma, array, arange, logical_not
1450        r=0
1451        nr = scan.nrow()
1452        a0,b0 = -1,-1
1453        allxlim = []
1454        allylim = []
1455        y=[]
1456        self._plotter.set_panels()
1457        self._plotter.palette(0)
1458        #title
1459        #xlab = self._abcissa and self._abcissa[panelcount] \
1460        #       or scan._getabcissalabel()
1461        #ylab = self._ordinate and self._ordinate[panelcount] \
1462        #       or scan._get_ordinate_label()
1463        xlab = self._abcissa or 'row number' #or Time
1464        ylab = self._ordinate or scan._get_ordinate_label()
1465        self._plotter.set_axes('xlabel',xlab)
1466        self._plotter.set_axes('ylabel',ylab)
1467        lbl = self._get_label(scan, r, 's', self._title)
1468        if isinstance(lbl, list) or isinstance(lbl, tuple):
1469        #    if 0 <= panelcount < len(lbl):
1470        #        lbl = lbl[panelcount]
1471        #    else:
1472                # get default label
1473             lbl = self._get_label(scan, r, self._panelling, None)
1474        self._plotter.set_axes('title',lbl)
1475        y=array(scan._get_column(scan._getspectrum,-1))
1476        m = array(scan._get_column(scan._getmask,-1))
1477        y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1478        x = arange(len(y))
1479        # try to handle spectral data somewhat...
1480        l,m = y.shape
1481        if m > 1:
1482            y=y.mean(axis=1)
1483        plotit = self._plotter.plot
1484        llbl = self._get_label(scan, r, self._stacking, None)
1485        self._plotter.set_line(label=llbl)
1486        if len(x) > 0:
1487            plotit(x,y)
1488
1489
1490    # forwards to matplotlib.Figure.text
1491    def figtext(self, *args, **kwargs):
1492        """
1493        Add text to figure at location x,y (relative 0-1 coords).
1494        This method forwards *args and **kwargs to a Matplotlib method,
1495        matplotlib.Figure.text.
1496        See the method help for detailed information.
1497        """
1498        self._assert_plotter(action="reload")
1499        self._plotter.text(*args, **kwargs)
1500    # end matplotlib.Figure.text forwarding function
1501
1502
1503    # printing header information
1504    @asaplog_post_dec
1505    def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
1506        """
1507        print data (scantable) header on the plot and/or logger.
1508        To plot the header on the plot, this method should be called after
1509        plotting spectra by the method, asapplotter.plot.
1510        Parameters:
1511            plot:      whether or not print header info on the plot.
1512            fontsize:  header font size (valid only plot=True)
1513            logger:    whether or not print header info on the logger.
1514            selstr:    additional selection string (not verified)
1515            extrastr:  additional string to print at the beginning (not verified)
1516        """
1517        if not plot and not logger:
1518            return
1519        if not self._data:
1520            raise RuntimeError("No scantable has been set yet.")
1521        # Now header will be printed on plot and/or logger.
1522        # Get header information and format it.
1523        ssum=self._data._list_header()
1524        # Print Observation header to the upper-left corner of plot
1525        headstr=[ssum[0:ssum.find('Obs. Type:')]]
1526        headstr.append(ssum[ssum.find('Obs. Type:'):ssum.find('Flux Unit:')])
1527        if extrastr != '':
1528            headstr[0]=extrastr+'\n'+headstr[0]
1529            self._headtext['extrastr'] = extrastr
1530        if selstr != '':
1531            selstr += '\n'
1532            self._headtext['selstr'] = selstr
1533        ssel=(selstr+self._data.get_selection().__str__()+self._selection.__str__() or 'none')
1534        headstr.append('***Selections***\n'+ssel)
1535
1536        if plot:
1537            errmsg = "Can plot header only after the first call to plot()."
1538            self._assert_plotter(action="halt",errmsg=errmsg)
1539            self._plotter.hold()
1540            self._header_plot(headstr,fontsize=fontsize)
1541            import time
1542            self._plotter.figure.text(0.99,0.01,
1543                            time.strftime("%a %d %b %Y  %H:%M:%S %Z"),
1544                            horizontalalignment='right',
1545                            verticalalignment='bottom',fontsize=8)
1546            self._plotter.release()
1547        if logger:
1548            selstr = "Selections:    "+ssel
1549            asaplog.push("----------------\n  Plot Summary\n----------------")
1550            asaplog.push(extrastr)
1551            asaplog.push(ssum[0:ssum.find('Selection:')]\
1552                         + selstr)
1553        self._headtext['string'] = headstr
1554        del ssel, ssum, headstr
1555
1556    def _header_plot(self, texts, fontsize=9):
1557        self._headtext['textobj']=[]
1558        nstcol=len(texts)
1559        for i in range(nstcol):
1560            self._headtext['textobj'].append(
1561                self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1562                                          texts[i],
1563                                          horizontalalignment='left',
1564                                          verticalalignment='top',
1565                                          fontsize=fontsize))
1566
1567    def clear_header(self):
1568        if not self._headtext['textobj']:
1569            asaplog.push("No header has been plotted. Exit without any operation")
1570            asaplog.post("WARN")
1571        elif self._assert_plotter(action="status"):
1572            self._plotter.hold()
1573            for textobj in self._headtext['textobj']:
1574                #if textobj.get_text() in self._headstring:
1575                try:
1576                    textobj.remove()
1577                except NotImplementedError:
1578                    self._plotter.figure.texts.pop(self._plotter.figure.texts.index(textobj))
1579            self._plotter.release()
1580        self._reset_header()
1581
1582    # plot spectra by pointing
1583    @asaplog_post_dec
1584    def plotgrid(self, scan=None,center=None,spacing=None,rows=None,cols=None):
1585        """
1586        Plot spectra based on direction.
1587       
1588        Parameters:
1589            scan:      a scantable to plot
1590            center:    the grid center direction (a list) of plots in the
1591                       unit of DIRECTION column.
1592                       (default) the center of map region
1593            spacing:   a list of horizontal (R.A.) and vertical (Dec.)
1594                       spacing in the unit of DIRECTION column.
1595                       (default) Calculated by the extent of map region and
1596                       the number of rows and cols to cover
1597            rows:      number of panels (grid points) in horizontal direction
1598            cols:      number of panels (grid points) in vertical direction
1599
1600        Note:
1601        - Only the first IFNO, POLNO, and BEAM in the scantable will be
1602        plotted.
1603        - This method doesn't re-grid and average spectra in scantable. Use
1604        asapgrid module to re-grid spectra before plotting with this method.
1605        Only the first spectrum is plotted in case there are multiple
1606        spectra which belong to a grid.
1607        """
1608        from asap import scantable
1609        from numpy import array, ma, cos
1610        if not self._data and not scan:
1611            msg = "No scantable is specified to plot"
1612            raise TypeError(msg)
1613        if scan:
1614            self.set_data(scan, refresh=False)
1615            del scan
1616
1617        # Rows and cols
1618        if rows:
1619            self._rows = int(rows)
1620        else:
1621            msg = "Number of rows to plot are not specified. "
1622            if self._rows:
1623                msg += "Using previous value = %d" % (self._rows)
1624                asaplog.push(msg)
1625            else:
1626                self._rows = 1
1627                msg += "Setting rows = %d" % (self._rows)
1628                asaplog.post()
1629                asaplog.push(msg)
1630                asaplog.post("WARN")
1631        if cols:
1632            self._cols = int(cols)
1633        else:
1634            msg = "Number of cols to plot are not specified. "
1635            if self._cols:
1636                msg += "Using previous value = %d" % (self._cols)
1637                asaplog.push(msg)
1638            else:
1639                self._cols = 1
1640                msg += "Setting cols = %d" % (self._cols)
1641                asaplog.post()
1642                asaplog.push(msg)
1643                asaplog.post("WARN")
1644
1645        # Center and spacing
1646        if center is None:
1647            #asaplog.post()
1648            asaplog.push("Grid center is not specified. Automatically calculated from pointing center.")
1649            #asaplog.post("WARN")
1650            dirarr = array(self._data.get_directionval()).transpose()
1651            #center = [dirarr[0].mean(), dirarr[1].mean()]
1652            center = [0.5*(dirarr[0].max() + dirarr[0].min()),
1653                      0.5*(dirarr[1].max() + dirarr[1].min())]
1654            del dirarr
1655        elif (type(center) in (list, tuple)) and len(center) > 1:
1656            center = center[0:2]
1657        else:
1658            msg = "Direction of grid center should be a list of float (R.A., Dec.)"
1659            raise ValueError, msg
1660        asaplog.push("Grid center: (%f, %f) " % (center[0],center[1]))
1661
1662        if spacing is None:
1663            #asaplog.post()
1664            asaplog.push("Grid spacing not specified. Automatically calculated from map coverage")
1665            #asaplog.post("WARN")
1666            # automatically get spacing
1667            dirarr = array(self._data.get_directionval()).transpose()
1668            wx = 2. * max(abs(dirarr[0].max()-center[0]),
1669                          abs(dirarr[0].min()-center[0]))
1670            wy = 2. * max(abs(dirarr[1].max()-center[1]),
1671                          abs(dirarr[1].min()-center[1]))
1672            ## slightly expand area to plot the edges
1673            #wx *= 1.01
1674            #wy *= 1.01
1675            #xgrid = wx/self._cols
1676            #ygrid = wy/self._rows
1677            xgrid = wx/max(self._cols-1.,1.)
1678            ygrid = wy/max(self._rows-1.,1.)
1679            #print "Pointing range: (x, y) = (%f - %f, %f - %f)" %\
1680            #  (dirarr[0].min(),dirarr[0].max(),dirarr[1].min(),dirarr[1].max())
1681            # identical R.A. and/or Dec. for all spectra.
1682            if xgrid == 0:
1683                xgrid = 1.
1684            if ygrid == 0:
1685                ygrid = 1.
1686            # spacing should be negative to transpose plot
1687            spacing = [- xgrid, - ygrid]
1688            del dirarr, xgrid, ygrid
1689        #elif isinstance(spacing, str):
1690        #    # spacing is a quantity
1691        elif (type(spacing) in (list, tuple)) and len(spacing) > 1:
1692            for i in xrange(2):
1693                val = spacing[i]
1694                if not isinstance(val, float):
1695                    raise TypeError("spacing should be a list of float")
1696                if val > 0.:
1697                    spacing[i] = -val
1698            spacing = spacing[0:2]
1699            # Correction of Dec. effect
1700            spacing[0] /= cos(center[1])
1701        else:
1702            msg = "Invalid spacing."
1703            raise TypeError(msg)
1704        asaplog.push("Spacing: (%f, %f) (projected)" % (spacing[0],spacing[1]))
1705
1706        ntotpl = self._rows * self._cols
1707        minpos = [center[0]-spacing[0]*self._cols/2.,
1708                  center[1]-spacing[1]*self._rows/2.]
1709        #print "Plot range: (x, y) = (%f - %f, %f - %f)" %\
1710        #      (minpos[0],minpos[0]+spacing[0]*self._cols,
1711        #       minpos[1],minpos[1]+spacing[1]*self._rows)
1712        ifs = self._data.getifnos()
1713        if len(ifs) > 1:
1714            msg = "Found multiple IFs in scantable. Only the first IF (IFNO=%d) will be plotted." % ifs[0]
1715            asaplog.post()
1716            asaplog.push(msg)
1717            asaplog.post("WARN")
1718        pols = self._data.getpolnos()
1719        if len(pols) > 1:
1720            msg = "Found multiple POLs in scantable. Only the first POL (POLNO=%d) will be plotted." % pols[0]
1721            asaplog.post()
1722            asaplog.push(msg)
1723            asaplog.post("WARN")
1724        beams = self._data.getbeamnos()
1725        if len(beams) > 1:
1726            msg = "Found multiple BEAMs in scantable. Only the first BEAM (BEAMNO=%d) will be plotted." % beams[0]
1727            asaplog.post()
1728            asaplog.push(msg)
1729            asaplog.post("WARN")
1730        self._data.set_selection(ifs=[ifs[0]],pols=[pols[0]],beams=[beams[0]])
1731        if self._data.nrow() > ntotpl:
1732            msg = "Scantable is finely sampled than plotting grids. "\
1733                  + "Only the first spectrum is plotted in each grid."
1734            asaplog.post()
1735            asaplog.push(msg)
1736            asaplog.post("WARN")
1737       
1738        self._assert_plotter(action="reload")
1739        self._plotter.hold()
1740        self._plotter.clear()
1741        self._plotter.legend()
1742       
1743        # Adjust subplot margins
1744        if not self._margins or len(self._margins) !=6:
1745            self.set_margin(refresh=False)
1746        self._plotter.set_panels(rows=self._rows,cols=self._cols,
1747                                 nplots=ntotpl,margin=self._margins,ganged=True)
1748        if self.casabar_exists():
1749            self._plotter.figmgr.casabar.set_pagecounter(1)
1750            self._plotter.figmgr.casabar.enable_button()
1751        # Actual plot
1752        npl = 0
1753        for irow in range(self._data.nrow()):
1754            pos = self._data.get_directionval(irow)
1755            ix = int((pos[0] - minpos[0])/spacing[0])
1756            if ix < 0 or ix >= self._cols:
1757                #print "Row %d : Out of X-range (x = %f) ... skipped" % (irow, pos[0])
1758                continue
1759            iy = int((pos[1]- minpos[1])/spacing[1])
1760            if iy < 0 or iy >= self._cols:
1761                #print "Row %d : Out of Y-range (y = %f) ... skipped" % (irow,pos[1])
1762                continue
1763            ipanel = ix + iy*self._cols
1764            if len(self._plotter.subplots[ipanel]['lines']) > 0:
1765                #print "Row %d : panel %d lready plotted ... skipped" % (irow,ipanel)
1766                # a spectrum already plotted in the panel
1767                continue
1768            # Plotting this row
1769            #print "PLOTTING row %d (panel=%d)" % (irow, ipanel)
1770            npl += 1
1771            self._plotter.subplot(ipanel)
1772            self._plotter.palette(0,colormap=self._colormap, \
1773                                  linestyle=0,linestyles=self._linestyles)
1774            xlab = self._abcissa and self._abcissa[ipanel] \
1775                   or self._data._getabcissalabel(irow)
1776            if self._offset and not self._abcissa:
1777                xlab += " (relative)"
1778            ylab = self._ordinate and self._ordinate[ipanel] \
1779                   or self._data._get_ordinate_label()
1780            self._plotter.set_axes('xlabel', xlab)
1781            self._plotter.set_axes('ylabel', ylab)
1782            #from numpy import pi
1783            #lbl = "(%f, %f)" % (self._data.get_directionval(irow)[0]*180/pi,self._data.get_directionval(irow)[1]*180./pi)
1784            lbl = self._data.get_direction(irow)
1785            self._plotter.set_axes('title',lbl)
1786
1787            y = self._data._getspectrum(irow)
1788            # flag application
1789            mr = self._data._getflagrow(irow)
1790            if mr:  # FLAGROW=True
1791                y = ma.masked_array(y,mask=mr)
1792            else:
1793                m = self._data._getmask(irow)
1794                from numpy import logical_not, logical_and
1795                ### user mask is not available so far
1796                #if self._maskselection and len(self._usermask) == len(m):
1797                #    if d[self._stacking](irow) in self._maskselection[self._stacking]:
1798                #            m = logical_and(m, self._usermask)
1799                y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1800
1801            x = array(self._data._getabcissa(irow))
1802            if self._offset:
1803                x += self._offset
1804            if self._minmaxx is not None:
1805                s,e = self._slice_indeces(x)
1806                x = x[s:e]
1807                y = y[s:e]
1808            if len(x) > 1024 and rcParams['plotter.decimate']:
1809                fac = len(x)/1024
1810                x = x[::fac]
1811                y = y[::fac]
1812            self._plotter.set_line(label=lbl)
1813            plotit = self._plotter.plot
1814            if self._hist: plotit = self._plotter.hist
1815            if len(x) > 0 and not mr:
1816                plotit(x,y)
1817#                 xlim= self._minmaxx or [min(x),max(x)]
1818#                 allxlim += xlim
1819#                 ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
1820#                 allylim += ylim
1821#             else:
1822#                 xlim = self._minmaxx or []
1823#                 allxlim += xlim
1824#                 ylim= self._minmaxy or []
1825#                 allylim += ylim
1826           
1827            if npl >= ntotpl:
1828                break
1829           
1830        if self._minmaxy is not None:
1831            self._plotter.set_limits(ylim=self._minmaxy)
1832        self._plotter.release()
1833        self._plotter.tidy()
1834        self._plotter.show(hardrefresh=False)
1835        return
Note: See TracBrowser for help on using the repository browser.