source: trunk/python/asapplotter.py @ 3017

Last change on this file since 3017 was 3016, checked in by Kana Sugimoto, 10 years ago

New Development: No

JIRA Issue: Yes (CAS-CAS-6564)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: run sdplot with plottype='azel', zoom-in and out to see if the tick interval is kept proper.

Put in Release Notes: No

Module(s): sdplot, asapplotter

Description: Switched off definition of the manual, fixed tick interval in asapplotter.plotazel(). The tick interval is set automatically by matplotlib.


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.5 KB
RevLine 
[1824]1from asap.parameters import rcParams
2from asap.selector import selector
3from asap.scantable import scantable
[1862]4from asap.logging import asaplog, asaplog_post_dec
[1153]5import matplotlib.axes
[1556]6from matplotlib.font_manager import FontProperties
7from matplotlib.text import Text
[2535]8from matplotlib import _pylab_helpers
[1556]9
[1317]10import re
[203]11
[2150]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
[203]32class asapplotter:
[226]33    """
34    The ASAP plotter.
35    By default the plotter is set up to plot polarisations
36    'colour stacked' and scantables across panels.
[1858]37
38    .. note::
39
[226]40        Currenly it only plots 'spectra' not Tsys or
41        other variables.
[1858]42
[226]43    """
[1563]44    def __init__(self, visible=None , **kwargs):
[734]45        self._visible = rcParams['plotter.gui']
46        if visible is not None:
47            self._visible = visible
[2451]48        self._plotter = None
49        self._inikwg = kwargs
[710]50
[2699]51        ### plot settings
[2698]52        self._colormap = None
53        self._linestyles = None
54        self._fp = FontProperties()
55        self._rows = None
56        self._cols = None
57        self._minmaxx = None
58        self._minmaxy = None
59        self._margins = self.set_margin(refresh=False)
60        self._legendloc = None
[2699]61        ### scantable plot settings
[2704]62        self._plotmode = "spectra"
[554]63        self._panelling = None
64        self._stacking = None
65        self.set_panelling()
66        self.set_stacking()
[2698]67        self._hist = rcParams['plotter.histogram']
[2699]68        ### scantable dependent settings
[203]69        self._data = None
[2698]70        self._abcunit = None
71        self._headtext = {'string': None, 'textobj': None}
72        self._selection = selector()
73        self._usermask = []
74        self._maskselection = None
75        self._offset = None
[607]76        self._lmap = None
[226]77        self._title = None
[257]78        self._ordinate = None
79        self._abcissa = None
[2699]80        ### cursors for page iteration
[1981]81        self._startrow = 0
82        self._ipanel = -1
83        self._panelrows = []
[1023]84
[920]85    def _translate(self, instr):
[1910]86        keys = "s b i p t r".split()
[920]87        if isinstance(instr, str):
88            for key in keys:
89                if instr.lower().startswith(key):
90                    return key
91        return None
92
[2535]93    @asaplog_post_dec
[2451]94    def _reload_plotter(self):
95        if self._plotter is not None:
[2535]96            #if not self._plotter.is_dead:
97            #    # clear lines and axes
98            #    try:
99            #        self._plotter.clear()
100            #    except: # Can't remove when already killed.
101            #        pass
[2451]102            if self.casabar_exists():
103                del self._plotter.figmgr.casabar
104            self._plotter.quit()
105            del self._plotter
[2535]106        asaplog.push('Loading new plotter')
[2451]107        self._plotter = new_asaplot(self._visible,**self._inikwg)
108        self._plotter.figmgr.casabar=self._new_custombar()
109        # just to make sure they're set
110        self._plotter.palette(color=0,colormap=self._colormap,
111                              linestyle=0,linestyles=self._linestyles)
112        self._plotter.legend(self._legendloc)
[710]113
[2714]114    ### TODO: it's probably better to define following two methods in
115    ###       backend dependent class.
[2173]116    def _new_custombar(self):
[1819]117        backend=matplotlib.get_backend()
[2168]118        if not self._visible:
119            return None
120        elif backend == "TkAgg":
[2155]121            from asap.customgui_tkagg import CustomToolbarTkAgg
[1819]122            return CustomToolbarTkAgg(self)
[2168]123        elif backend == "Qt4Agg":
124            from asap.customgui_qt4agg import CustomToolbarQT4Agg
125            return CustomToolbarQT4Agg(self)
[1995]126        return None
[1819]127
[2147]128    def casabar_exists(self):
129        if not hasattr(self._plotter.figmgr,'casabar'):
130            return False
131        elif self._plotter.figmgr.casabar:
132            return True
133        return False
[2714]134    ### end of TODO
[2147]135
[2453]136    def _assert_plotter(self,action="status",errmsg=None):
[2451]137        """
[2453]138        Check plot window status. Returns True if plot window is alive.
[2451]139        Parameters
[2453]140            action:    An action to take if the plotter window is not alive.
141                       ['status'|'reload'|'halt']
142                       The action 'status' simply returns False if asaplot
143                       is not alive. When action='reload', plot window is
144                       reloaded and the method returns True. Finally, an
145                       error is raised when action='halt'.
[2451]146            errmsg:    An error (warning) message to send to the logger,
[2453]147                       when plot window is not alive.
[2451]148        """
[2538]149        isAlive = (self._plotter is not None) and self._plotter._alive()
[2535]150        # More tests
[2538]151        #if isAlive:
152        #    if self._plotter.figmgr:
153        #        figmgr = self._plotter.figmgr
154        #        figid = figmgr.num
155        #        # Make sure figid=0 is what asapplotter expects.
156        #        # It might be already destroied/overridden by matplotlib
157        #        # commands or other plotting methods using asaplot.
158        #        isAlive = _pylab_helpers.Gcf.has_fignum(figid) and \
159        #                  (figmgr == \
160        #                   _pylab_helpers.Gcf.get_fig_manager(figid))
161        #    else:
162        #        isAlive = False
[2535]163           
164        if isAlive:
[2451]165            return True
166        # Plotter is not alive.
167        haltmsg = "Plotter window has not yet been loaded or is closed."
168        if type(errmsg)==str and len(errmsg) > 0:
169            haltmsg = errmsg
170       
[2453]171        if action.upper().startswith("R"):
[2451]172            # reload plotter
173            self._reload_plotter()
174            return True
[2453]175        elif action.upper().startswith("H"):
[2451]176            # halt
177            asaplog.push(haltmsg)
178            asaplog.post("ERROR")
179            raise RuntimeError(haltmsg)
180        else:
181            if errmsg:
182                asaplog.push(errmsg)
183                asaplog.post("WARN")
184            return False
185
186
[1572]187    def gca(self):
[2451]188        errmsg = "No axis to retun. Need to plot first."
[2453]189        if not self._assert_plotter(action="status",errmsg=errmsg):
[2451]190            return None
[1572]191        return self._plotter.figure.gca()
192
[1550]193    def refresh(self):
[1572]194        """Do a soft refresh"""
[2451]195        errmsg = "No figure to re-plot. Need to plot first."
[2453]196        self._assert_plotter(action="halt",errmsg=errmsg)
[2451]197
[1550]198        self._plotter.figure.show()
199
[2698]200    def save(self, filename=None, orientation=None, dpi=None):
201        """
202        Save the plot to a file. The known formats are 'png', 'ps', 'eps'.
203        Parameters:
204             filename:    The name of the output file. This is optional
205                          and autodetects the image format from the file
206                          suffix. If non filename is specified a file
207                          called 'yyyymmdd_hhmmss.png' is created in the
208                          current directory.
209             orientation: optional parameter for postscript only (not eps).
210                          'landscape', 'portrait' or None (default) are valid.
211                          If None is choosen for 'ps' output, the plot is
212                          automatically oriented to fill the page.
213             dpi:         The dpi of the output non-ps plot
214        """
215        errmsg = "Cannot save figure. Need to plot first."
216        self._assert_plotter(action="halt",errmsg=errmsg)
217       
218        self._plotter.save(filename,orientation,dpi)
219        return
220
[1555]221    def create_mask(self, nwin=1, panel=0, color=None):
[1597]222        """
[1927]223        Interactively define a mask. It retruns a mask that is equivalent to
[1597]224        the one created manually with scantable.create_mask.
225        Parameters:
226            nwin:       The number of mask windows to create interactively
227                        default is 1.
228            panel:      Which panel to use for mask selection. This is useful
229                        if different IFs are spread over panels (default 0)
230        """
[2451]231        ## this method relies on already plotted figure
[2453]232        if not self._assert_plotter(action="status") or (self._data is None):
[2451]233            msg = "Cannot create mask interactively on plot. Can only create mask after plotting."
234            asaplog.push( msg )
235            asaplog.post( "ERROR" )
[1555]236            return []
[1547]237        outmask = []
[1549]238        self._plotter.subplot(panel)
239        xmin, xmax = self._plotter.axes.get_xlim()
[1548]240        marg = 0.05*(xmax-xmin)
[1549]241        self._plotter.axes.set_xlim(xmin-marg, xmax+marg)
[1550]242        self.refresh()
[1695]243
[1555]244        def cleanup(lines=False, texts=False, refresh=False):
245            if lines:
246                del self._plotter.axes.lines[-1]
247            if texts:
248                del self._plotter.axes.texts[-1]
249            if refresh:
250                self.refresh()
251
252        for w in xrange(nwin):
[1547]253            wpos = []
[1695]254            self.text(0.05,1.0, "Add start boundary",
[1555]255                      coords="relative", fontsize=10)
256            point = self._plotter.get_point()
257            cleanup(texts=True)
258            if point is None:
259                continue
260            wpos.append(point[0])
[1695]261            self.axvline(wpos[0], color=color)
[1551]262            self.text(0.05,1.0, "Add end boundary", coords="relative", fontsize=10)
[1555]263            point = self._plotter.get_point()
264            cleanup(texts=True, lines=True)
265            if point is None:
266                self.refresh()
267                continue
268            wpos.append(point[0])
269            self.axvspan(wpos[0], wpos[1], alpha=0.1,
270                         edgecolor=color, facecolor=color)
271            ymin, ymax = self._plotter.axes.get_ylim()
[1547]272            outmask.append(wpos)
[1153]273
[1555]274        self._plotter.axes.set_xlim(xmin, xmax)
275        self.refresh()
276        if len(outmask) > 0:
277            return self._data.create_mask(*outmask)
278        return []
279
[2699]280
[2714]281    ### Forwards to methods in matplotlib axes ###
[1153]282    def text(self, *args, **kwargs):
[2453]283        self._assert_plotter(action="reload")
[1547]284        if kwargs.has_key("interactive"):
285            if kwargs.pop("interactive"):
286                pos = self._plotter.get_point()
287                args = tuple(pos)+args
[1153]288        self._axes_callback("text", *args, **kwargs)
[1547]289
[1358]290    text.__doc__ = matplotlib.axes.Axes.text.__doc__
[1559]291
[1153]292    def arrow(self, *args, **kwargs):
[2453]293        self._assert_plotter(action="reload")
[1547]294        if kwargs.has_key("interactive"):
295            if kwargs.pop("interactive"):
296                pos = self._plotter.get_region()
297                dpos = (pos[0][0], pos[0][1],
298                        pos[1][0]-pos[0][0],
299                        pos[1][1] - pos[0][1])
300                args = dpos + args
[1153]301        self._axes_callback("arrow", *args, **kwargs)
[1547]302
[1358]303    arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
[1559]304
305    def annotate(self, text, xy=None, xytext=None, **kwargs):
[2453]306        self._assert_plotter(action="reload")
[1559]307        if kwargs.has_key("interactive"):
308            if kwargs.pop("interactive"):
309                xy = self._plotter.get_point()
310                xytext = self._plotter.get_point()
311        if not kwargs.has_key("arrowprops"):
312            kwargs["arrowprops"] = dict(arrowstyle="->")
313        self._axes_callback("annotate", text, xy, xytext, **kwargs)
314
315    annotate.__doc__ = matplotlib.axes.Axes.annotate.__doc__
316
[1153]317    def axvline(self, *args, **kwargs):
[2453]318        self._assert_plotter(action="reload")
[1547]319        if kwargs.has_key("interactive"):
320            if kwargs.pop("interactive"):
321                pos = self._plotter.get_point()
322                args = (pos[0],)+args
[1153]323        self._axes_callback("axvline", *args, **kwargs)
[1559]324
[1358]325    axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
[1547]326
[1153]327    def axhline(self, *args, **kwargs):
[2453]328        self._assert_plotter(action="reload")
[1547]329        if kwargs.has_key("interactive"):
330            if kwargs.pop("interactive"):
331                pos = self._plotter.get_point()
332                args = (pos[1],)+args
[1153]333        self._axes_callback("axhline", *args, **kwargs)
[1559]334
[1358]335    axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
[1547]336
[1153]337    def axvspan(self, *args, **kwargs):
[2453]338        self._assert_plotter(action="reload")
[1547]339        if kwargs.has_key("interactive"):
340            if kwargs.pop("interactive"):
341                pos = self._plotter.get_region()
342                dpos = (pos[0][0], pos[1][0])
343                args = dpos + args
[1153]344        self._axes_callback("axvspan", *args, **kwargs)
345        # hack to preventy mpl from redrawing the patch
346        # it seem to convert the patch into lines on every draw.
347        # This doesn't happen in a test script???
[1547]348        #del self._plotter.axes.patches[-1]
349
[1358]350    axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
[1232]351
[1153]352    def axhspan(self, *args, **kwargs):
[2453]353        self._assert_plotter(action="reload")
[1547]354        if kwargs.has_key("interactive"):
355            if kwargs.pop("interactive"):
356                pos = self._plotter.get_region()
357                dpos = (pos[0][1], pos[1][1])
358                args = dpos + args
[1232]359        self._axes_callback("axhspan", *args, **kwargs)
[1153]360        # hack to preventy mpl from redrawing the patch
361        # it seem to convert the patch into lines on every draw.
362        # This doesn't happen in a test script???
[1547]363        #del self._plotter.axes.patches[-1]
[1559]364
[1358]365    axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
[1153]366
367    def _axes_callback(self, axesfunc, *args, **kwargs):
[2453]368        self._assert_plotter(action="reload")
[1153]369        panel = 0
370        if kwargs.has_key("panel"):
371            panel = kwargs.pop("panel")
372        coords = None
373        if kwargs.has_key("coords"):
374            coords = kwargs.pop("coords")
375            if coords.lower() == 'world':
376                kwargs["transform"] = self._plotter.axes.transData
377            elif coords.lower() == 'relative':
378                kwargs["transform"] = self._plotter.axes.transAxes
379        self._plotter.subplot(panel)
380        self._plotter.axes.set_autoscale_on(False)
381        getattr(self._plotter.axes, axesfunc)(*args, **kwargs)
382        self._plotter.show(False)
383        self._plotter.axes.set_autoscale_on(True)
384    # end matplotlib.axes fowarding functions
385
[2699]386
387    ### Forwards to matplotlib.Figure.text ###
[2698]388    def figtext(self, *args, **kwargs):
389        """
390        Add text to figure at location x,y (relative 0-1 coords).
391        This method forwards *args and **kwargs to a Matplotlib method,
392        matplotlib.Figure.text.
393        See the method help for detailed information.
394        """
395        self._assert_plotter(action="reload")
396        self._plotter.text(*args, **kwargs)
397    # end matplotlib.Figure.text forwarding function
398
[2699]399
400    ### Set Plot parameters ###
[1862]401    @asaplog_post_dec
[1819]402    def set_data(self, scan, refresh=True):
403        """
[1824]404        Set a scantable to plot.
[1819]405        Parameters:
406            scan:      a scantable
407            refresh:   True (default) or False. If True, the plot is
[1824]408                       replotted based on the new parameter setting(s).
[1819]409                       Otherwise,the parameter(s) are set without replotting.
410        Note:
411           The user specified masks and data selections will be reset
412           if a new scantable is set. This method should be called before
[1824]413           setting data selections (set_selection) and/or masks (set_mask).
[1819]414        """
415        from asap import scantable
416        if isinstance(scan, scantable):
[2604]417            if (self._data is not None) and (scan != self._data):
418                del self._data
419                msg = "A new scantable is set to the plotter. "\
[2714]420                      "The masks, data selections, and labels are reset."
421                asaplog.push(msg)
[2604]422            self._data = scan
423            # reset
424            self._reset()
[1819]425        else:
426            msg = "Input is not a scantable"
427            raise TypeError(msg)
[1547]428
[1819]429        # ranges become invalid when unit changes
430        if self._abcunit and self._abcunit != self._data.get_unit():
431            self._minmaxx = None
432            self._minmaxy = None
433            self._abcunit = self._data.get_unit()
434        if refresh: self.plot()
435
[1862]436    @asaplog_post_dec
[1819]437    def set_mode(self, stacking=None, panelling=None, refresh=True):
[203]438        """
[377]439        Set the plots look and feel, i.e. what you want to see on the plot.
[203]440        Parameters:
441            stacking:     tell the plotter which variable to plot
[1217]442                          as line colour overlays (default 'pol')
[203]443            panelling:    tell the plotter which variable to plot
444                          across multiple panels (default 'scan'
[1819]445            refresh:      True (default) or False. If True, the plot is
[1824]446                          replotted based on the new parameter setting(s).
[1819]447                          Otherwise,the parameter(s) are set without replotting.
[203]448        Note:
449            Valid modes are:
450                 'beam' 'Beam' 'b':     Beams
451                 'if' 'IF' 'i':         IFs
452                 'pol' 'Pol' 'p':       Polarisations
453                 'scan' 'Scan' 's':     Scans
454                 'time' 'Time' 't':     Times
[1989]455                 'row' 'Row' 'r':       Rows
456            When either 'stacking' or 'panelling' is set to 'row',
457            the other parameter setting is ignored.
[203]458        """
[753]459        msg = "Invalid mode"
460        if not self.set_panelling(panelling) or \
461               not self.set_stacking(stacking):
[1859]462            raise TypeError(msg)
[1989]463        #if self._panelling == 'r':
464        #    self._stacking = '_r'
465        #if self._stacking == 'r':
466        #    self._panelling = '_r'
[1819]467        if refresh and self._data: self.plot(self._data)
[203]468        return
469
[2698]470    def set_stacking(self, what=None):
471        """Set the 'stacking' mode i.e. which type of spectra should be
472        overlayed.
473        """
474        mode = what
475        if mode is None:
476             mode = rcParams['plotter.stacking']
477        md = self._translate(mode)
478        if md:
479            self._stacking = md
480            self._lmap = None
481            # new mode is set. need to reset counters for multi page plotting
482            self._reset_counters()
483            return True
484        return False
485
[554]486    def set_panelling(self, what=None):
[1858]487        """Set the 'panelling' mode i.e. which type of spectra should be
488        spread across different panels.
489        """
490
[554]491        mode = what
492        if mode is None:
493             mode = rcParams['plotter.panelling']
494        md = self._translate(mode)
[203]495        if md:
[554]496            self._panelling = md
[226]497            self._title = None
[2698]498            # new mode is set. need to reset counters for multi page plotting
[1981]499            self._reset_counters()
[203]500            return True
501        return False
502
[1819]503    def set_layout(self,rows=None,cols=None,refresh=True):
[377]504        """
505        Set the multi-panel layout, i.e. how many rows and columns plots
506        are visible.
507        Parameters:
508             rows:   The number of rows of plots
509             cols:   The number of columns of plots
[1819]510             refresh:  True (default) or False. If True, the plot is
[1824]511                       replotted based on the new parameter setting(s).
[1819]512                       Otherwise,the parameter(s) are set without replotting.
[377]513        Note:
514             If no argument is given, the potter reverts to its auto-plot
515             behaviour.
516        """
517        self._rows = rows
518        self._cols = cols
[2715]519        # new layout is set. need to reset counters for multi page plotting
520        self._reset_counters()
[1819]521        if refresh and self._data: self.plot(self._data)
[377]522        return
523
[1897]524    def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True, offset=None):
[203]525        """
526        Set the range of interest on the abcissa of the plot
527        Parameters:
[525]528            [x,y]start,[x,y]end:  The start and end points of the 'zoom' window
[1819]529            refresh:  True (default) or False. If True, the plot is
[1824]530                      replotted based on the new parameter setting(s).
[1819]531                      Otherwise,the parameter(s) are set without replotting.
[1897]532            offset:   shift the abcissa by the given amount. The abcissa label will
533                      have '(relative)' appended to it.
[203]534        Note:
535            These become non-sensical when the unit changes.
536            use plotter.set_range() without parameters to reset
537
538        """
[1897]539        self._offset = offset
[525]540        if xstart is None and xend is None:
541            self._minmaxx = None
[600]542        else:
543            self._minmaxx = [xstart,xend]
[525]544        if ystart is None and yend is None:
545            self._minmaxy = None
[600]546        else:
[709]547            self._minmaxy = [ystart,yend]
[1819]548        if refresh and self._data: self.plot(self._data)
[203]549        return
[709]550
[1819]551    def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
[203]552        """
553        Specify a mapping for the legend instead of using the default
554        indices:
555        Parameters:
[1101]556            mp:        a list of 'strings'. This should have the same length
557                       as the number of elements on the legend and then maps
558                       to the indeces in order. It is possible to uses latex
559                       math expression. These have to be enclosed in r'',
560                       e.g. r'$x^{2}$'
561            fontsize:  The font size of the label (default None)
562            mode:      where to display the legend
563                       Any other value for loc else disables the legend:
[1096]564                        0: auto
565                        1: upper right
566                        2: upper left
567                        3: lower left
568                        4: lower right
569                        5: right
570                        6: center left
571                        7: center right
572                        8: lower center
573                        9: upper center
574                        10: center
[1819]575            refresh:    True (default) or False. If True, the plot is
[1824]576                        replotted based on the new parameter setting(s).
[1819]577                        Otherwise,the parameter(s) are set without replotting.
[203]578
579        Example:
[485]580             If the data has two IFs/rest frequencies with index 0 and 1
[203]581             for CO and SiO:
582             plotter.set_stacking('i')
[710]583             plotter.set_legend(['CO','SiO'])
[203]584             plotter.plot()
[710]585             plotter.set_legend([r'$^{12}CO$', r'SiO'])
[203]586        """
587        self._lmap = mp
[2451]588        #self._plotter.legend(mode)
589        self._legendloc = mode
[1101]590        if isinstance(fontsize, int):
591            from matplotlib import rc as rcp
592            rcp('legend', fontsize=fontsize)
[1819]593        if refresh and self._data: self.plot(self._data)
[226]594        return
595
[1819]596    def set_title(self, title=None, fontsize=None, refresh=True):
[710]597        """
[2451]598        Set the title of sub-plots. If multiple sub-plots are plotted,
[710]599        multiple titles have to be specified.
[1819]600        Parameters:
[2451]601            title:      a list of titles of sub-plots.
602            fontsize:   a font size of titles (integer)
[1819]603            refresh:    True (default) or False. If True, the plot is
[1824]604                        replotted based on the new parameter setting(s).
[1819]605                        Otherwise,the parameter(s) are set without replotting.
[710]606        Example:
607             # two panels are visible on the plotter
[2451]608             plotter.set_title(['First Panel','Second Panel'])
[710]609        """
[226]610        self._title = title
[1101]611        if isinstance(fontsize, int):
612            from matplotlib import rc as rcp
613            rcp('axes', titlesize=fontsize)
[1819]614        if refresh and self._data: self.plot(self._data)
[226]615        return
616
[1819]617    def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
[710]618        """
619        Set the y-axis label of the plot. If multiple panels are plotted,
620        multiple labels have to be specified.
[1021]621        Parameters:
622            ordinate:    a list of ordinate labels. None (default) let
623                         data determine the labels
[2451]624            fontsize:    a font size of vertical axis labels (integer)
[1819]625            refresh:     True (default) or False. If True, the plot is
[1824]626                         replotted based on the new parameter setting(s).
[1819]627                         Otherwise,the parameter(s) are set without replotting.
[710]628        Example:
629             # two panels are visible on the plotter
[2451]630             plotter.set_ordinate(['First Y-Axis','Second Y-Axis'])
[710]631        """
[257]632        self._ordinate = ordinate
[1101]633        if isinstance(fontsize, int):
634            from matplotlib import rc as rcp
635            rcp('axes', labelsize=fontsize)
636            rcp('ytick', labelsize=fontsize)
[1819]637        if refresh and self._data: self.plot(self._data)
[257]638        return
639
[1819]640    def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
[710]641        """
642        Set the x-axis label of the plot. If multiple panels are plotted,
643        multiple labels have to be specified.
[1021]644        Parameters:
645            abcissa:     a list of abcissa labels. None (default) let
646                         data determine the labels
[2451]647            fontsize:    a font size of horizontal axis labels (integer)
[1819]648            refresh:     True (default) or False. If True, the plot is
[1824]649                         replotted based on the new parameter setting(s).
[1819]650                         Otherwise,the parameter(s) are set without replotting.
[710]651        Example:
652             # two panels are visible on the plotter
[2451]653             plotter.set_ordinate(['First X-Axis','Second X-Axis'])
[710]654        """
[257]655        self._abcissa = abcissa
[1101]656        if isinstance(fontsize, int):
657            from matplotlib import rc as rcp
658            rcp('axes', labelsize=fontsize)
659            rcp('xtick', labelsize=fontsize)
[1819]660        if refresh and self._data: self.plot(self._data)
[257]661        return
662
[2700]663    def set_histogram(self, hist=True, linewidth=None, refresh=True):
664        """
665        Enable/Disable histogram-like plotting.
666        Parameters:
667            hist:        True (default) or False. The fisrt default
668                         is taken from the .asaprc setting
669                         plotter.histogram
670            linewidth:   a line width
671            refresh:     True (default) or False. If True, the plot is
672                         replotted based on the new parameter setting(s).
673                         Otherwise,the parameter(s) are set without replotting.
674        """
675        self._hist = hist
676        if isinstance(linewidth, float) or isinstance(linewidth, int):
677            from matplotlib import rc as rcp
678            rcp('lines', linewidth=linewidth)
679        if refresh and self._data: self.plot(self._data)
680
[1819]681    def set_colors(self, colmap, refresh=True):
[377]682        """
[1217]683        Set the colours to be used. The plotter will cycle through
684        these colours when lines are overlaid (stacking mode).
[1021]685        Parameters:
[1217]686            colmap:     a list of colour names
[1819]687            refresh:    True (default) or False. If True, the plot is
[1824]688                        replotted based on the new parameter setting(s).
[1819]689                        Otherwise,the parameter(s) are set without replotting.
[710]690        Example:
[2451]691             plotter.set_colors('red green blue')
[710]692             # If for example four lines are overlaid e.g I Q U V
693             # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
694             # and 'V' will be 'red' again.
695        """
[2451]696        #if isinstance(colmap,str):
697        #    colmap = colmap.split()
698        #self._plotter.palette(0, colormap=colmap)
699        self._colormap = colmap
[1819]700        if refresh and self._data: self.plot(self._data)
[710]701
[1217]702    # alias for english speakers
703    set_colours = set_colors
704
[1819]705    def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
[710]706        """
[734]707        Set the linestyles to be used. The plotter will cycle through
708        these linestyles when lines are overlaid (stacking mode) AND
709        only one color has been set.
[710]710        Parameters:
[2451]711            linestyles:      a list of linestyles to use.
[710]712                             'line', 'dashed', 'dotted', 'dashdot',
713                             'dashdotdot' and 'dashdashdot' are
714                             possible
[2451]715            linewidth:       a line width
[1819]716            refresh:         True (default) or False. If True, the plot is
[1824]717                             replotted based on the new parameter setting(s).
[1819]718                             Otherwise,the parameter(s) are set without replotting.
[710]719        Example:
[2451]720             plotter.set_colors('black')
721             plotter.set_linestyles('line dashed dotted dashdot')
[710]722             # If for example four lines are overlaid e.g I Q U V
723             # 'I' will be 'solid', 'Q' will be 'dashed',
724             # U will be 'dotted' and 'V' will be 'dashdot'.
725        """
[2451]726        #if isinstance(linestyles,str):
727        #    linestyles = linestyles.split()
728        #self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
729        self._linestyles = linestyles
[1101]730        if isinstance(linewidth, float) or isinstance(linewidth, int):
731            from matplotlib import rc as rcp
732            rcp('lines', linewidth=linewidth)
[1819]733        if refresh and self._data: self.plot(self._data)
[710]734
[1819]735    def set_font(self, refresh=True,**kwargs):
[1101]736        """
737        Set font properties.
738        Parameters:
739            family:    one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
740            style:     one of 'normal' (or 'roman'), 'italic'  or 'oblique'
741            weight:    one of 'normal or 'bold'
742            size:      the 'general' font size, individual elements can be adjusted
743                       seperately
[1819]744            refresh:   True (default) or False. If True, the plot is
[1824]745                       replotted based on the new parameter setting(s).
[1819]746                       Otherwise,the parameter(s) are set without replotting.
[1101]747        """
748        from matplotlib import rc as rcp
[1547]749        fdict = {}
750        for k,v in kwargs.iteritems():
751            if v:
752                fdict[k] = v
[1556]753        self._fp = FontProperties(**fdict)
[1819]754        if refresh and self._data: self.plot(self._data)
[1101]755
[2037]756    def set_margin(self,margin=[],refresh=True):
[1819]757        """
[2037]758        Set margins between subplots and plot edges.
[1819]759        Parameters:
[2037]760            margin:   a list of margins in figure coordinate (0-1),
[1824]761                      i.e., fraction of the figure width or height.
[1819]762                      The order of elements should be:
763                      [left, bottom, right, top, horizontal space btw panels,
[1824]764                      vertical space btw panels].
[1819]765            refresh:  True (default) or False. If True, the plot is
[1824]766                      replotted based on the new parameter setting(s).
[1819]767                      Otherwise,the parameter(s) are set without replotting.
768        Note
[2037]769        * When margin is not specified, the values are reset to the defaults
[1819]770          of matplotlib.
[1824]771        * If any element is set to be None, the current value is adopted.
[1819]772        """
[2037]773        if margin == []: self._margins=self._reset_margin()
[1824]774        else:
[2037]775            self._margins=[None]*6
776            self._margins[0:len(margin)]=margin
777        #print "panel margin set to ",self._margins
[1819]778        if refresh and self._data: self.plot(self._data)
779
[2037]780    def _reset_margin(self):
[1819]781        ks=map(lambda x: 'figure.subplot.'+x,
782               ['left','bottom','right','top','hspace','wspace'])
783        return map(matplotlib.rcParams.get,ks)
784
[1259]785    def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
[1146]786                   location=None):
787        """
[1158]788        Plot a line catalog.
789        Parameters:
790            linecat:      the linecatalog to plot
[1168]791            doppler:      the velocity shift to apply to the frequencies
[1158]792            deltachan:    the number of channels to include each side of the
793                          line to determine a local maximum/minimum
[1927]794            rotate:       the rotation (in degrees) for the text label (default 90.0)
[1158]795            location:     the location of the line annotation from the 'top',
796                          'bottom' or alternate (None - the default)
[1165]797        Notes:
798        If the spectrum is flagged no line will be drawn in that location.
[1146]799        """
[2451]800        errmsg = "Cannot plot spectral lines. Need to plot scantable first."
[2453]801        self._assert_plotter(action="halt",errmsg=errmsg)
[1259]802        if not self._data:
803            raise RuntimeError("No scantable has been plotted yet.")
[1146]804        from asap._asap import linecatalog
[1259]805        if not isinstance(linecat, linecatalog):
806            raise ValueError("'linecat' isn't of type linecatalog.")
807        if not self._data.get_unit().endswith("Hz"):
808            raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
[1739]809        from numpy import ma
[1146]810        for j in range(len(self._plotter.subplots)):
811            self._plotter.subplot(j)
812            lims = self._plotter.axes.get_xlim()
[1153]813            for row in range(linecat.nrow()):
[1259]814                # get_frequency returns MHz
815                base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
816                restf = linecat.get_frequency(row)/base[self._data.get_unit()]
[1165]817                c = 299792.458
[1174]818                freq = restf*(1.0-doppler/c)
[1146]819                if lims[0] < freq < lims[1]:
820                    if location is None:
821                        loc = 'bottom'
[1153]822                        if row%2: loc='top'
[1146]823                    else: loc = location
[1153]824                    maxys = []
825                    for line in self._plotter.axes.lines:
826                        v = line._x
827                        asc = v[0] < v[-1]
828
829                        idx = None
830                        if not asc:
831                            if v[len(v)-1] <= freq <= v[0]:
832                                i = len(v)-1
833                                while i>=0 and v[i] < freq:
834                                    idx = i
835                                    i-=1
836                        else:
837                           if v[0] <= freq <= v[len(v)-1]:
838                                i = 0
839                                while  i<len(v) and v[i] < freq:
840                                    idx = i
841                                    i+=1
842                        if idx is not None:
843                            lower = idx - deltachan
844                            upper = idx + deltachan
845                            if lower < 0: lower = 0
846                            if upper > len(v): upper = len(v)
847                            s = slice(lower, upper)
[1167]848                            y = line._y[s]
[1165]849                            maxy = ma.maximum(y)
850                            if isinstance( maxy, float):
851                                maxys.append(maxy)
[1164]852                    if len(maxys):
853                        peak = max(maxys)
[1165]854                        if peak > self._plotter.axes.get_ylim()[1]:
855                            loc = 'bottom'
[1164]856                    else:
857                        continue
[1157]858                    self._plotter.vline_with_label(freq, peak,
859                                                   linecat.get_name(row),
860                                                   location=loc, rotate=rotate)
[1153]861        self._plotter.show(hardrefresh=False)
[1146]862
[1153]863
[2698]864    def set_selection(self, selection=None, refresh=True, **kw):
[710]865        """
[377]866        Parameters:
[2698]867            selection:  a selector object (default unset the selection)
868            refresh:    True (default) or False. If True, the plot is
869                        replotted based on the new parameter setting(s).
870                        Otherwise,the parameter(s) are set without replotting.
[377]871        """
[2698]872        if selection is None:
873            # reset
874            if len(kw) == 0:
875                self._selection = selector()
876            else:
877                # try keywords
878                for k in kw:
879                    if k not in selector.fields:
880                        raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
881                self._selection = selector(**kw)
882        elif isinstance(selection, selector):
883            self._selection = selection
884        else:
885            raise TypeError("'selection' is not of type selector")
[709]886
[2698]887        order = self._get_sortstring([self._panelling,self._stacking])
888        if order:
889            self._selection.set_order(order)
890        if refresh and self._data:
891            self.plot()
892
[1862]893    @asaplog_post_dec
[1819]894    def set_mask(self, mask=None, selection=None, refresh=True):
[525]895        """
[734]896        Set a plotting mask for a specific polarization.
[2451]897        This is useful for masking out 'noise' Pangle outside a source.
[734]898        Parameters:
[920]899             mask:           a mask from scantable.create_mask
900             selection:      the spectra to apply the mask to.
[1819]901             refresh:        True (default) or False. If True, the plot is
[1824]902                             replotted based on the new parameter setting(s).
[1819]903                             Otherwise,the parameter(s) are set without replotting.
[734]904        Example:
[920]905             select = selector()
[2451]906             select.setpolstrings('Pangle')
[920]907             plotter.set_mask(mymask, select)
[734]908        """
[710]909        if not self._data:
[920]910            msg = "Can only set mask after a first call to plot()"
[1859]911            raise RuntimeError(msg)
[2714]912        if (mask is not None) and len(mask):
[920]913            if isinstance(mask, list) or isinstance(mask, tuple):
914                self._usermask = array(mask)
[710]915            else:
[920]916                self._usermask = mask
917        if mask is None and selection is None:
918            self._usermask = []
919            self._maskselection = None
920        if isinstance(selection, selector):
[947]921            self._maskselection = {'b': selection.get_beams(),
922                                   's': selection.get_scans(),
923                                   'i': selection.get_ifs(),
924                                   'p': selection.get_pols(),
[920]925                                   't': [] }
[710]926        else:
[920]927            self._maskselection = None
[1819]928        if refresh: self.plot(self._data)
[710]929
[709]930
[2699]931    ### Reset methods ###
[710]932    def _reset(self):
[2714]933        """Reset method called when new data is set"""
934        # reset selections and masks
935        self.set_selection(None, False)
936        self.set_mask(None, None, False)
937        # reset offset
[1897]938        self._offset = None
[2714]939        # reset header
[2051]940        self._reset_header()
[2714]941        # reset labels
942        self._lmap = None # related to stack
943        self.set_title(None, None, False)
944        self.set_ordinate(None, None, False)
945        self.set_abcissa(None, None, False)
[920]946
[2051]947    def _reset_header(self):
[2053]948        self._headtext={'string': None, 'textobj': None}
[2051]949
[2697]950    def _reset_counter(self):
951        self._startrow = 0
952        self._ipanel = -1
[2715]953        self._panelrows = []
[2697]954        self._reset_header()
955        if self.casabar_exists():
956            self._plotter.figmgr.casabar.set_pagecounter(1)
957
[2698]958    def _reset_counters(self):
959        self._startrow = 0
960        self._ipanel = -1
961        self._panelrows = []
962
[2699]963
964    ### Actual scantable plot methods ###
[2698]965    @asaplog_post_dec
966    def plot(self, scan=None):
967        """
968        Plot a scantable.
969        Parameters:
970            scan:   a scantable
971        Note:
972            If a scantable was specified in a previous call
973            to plot, no argument has to be given to 'replot'
974            NO checking is done that the abcissas of the scantable
975            are consistent e.g. all 'channel' or all 'velocity' etc.
976        """
[2704]977        self._plotmode = "spectra"
[2698]978        if not self._data and not scan:
979            msg = "Input is not a scantable"
980            raise TypeError(msg)
981
982        self._assert_plotter(action="reload")
983        self._plotter.hold()
984        self._reset_counter()
985        #self._plotter.clear()
986        if scan:
987            self.set_data(scan, refresh=False)
988        self._plotter.palette(color=0,colormap=self._colormap,
989                              linestyle=0,linestyles=self._linestyles)
990        self._plotter.legend(self._legendloc)
991        self._plot(self._data)
992        if self._minmaxy is not None:
993            self._plotter.set_limits(ylim=self._minmaxy)
994        if self.casabar_exists(): self._plotter.figmgr.casabar.enable_button()
995        self._plotter.release()
996        self._plotter.tidy()
997        self._plotter.show(hardrefresh=False)
998        return
999
[920]1000    def _plot(self, scan):
[947]1001        savesel = scan.get_selection()
1002        sel = savesel +  self._selection
[1910]1003        order = self._get_sortstring([self._panelling,self._stacking])
1004        if order:
1005            sel.set_order(order)
[947]1006        scan.set_selection(sel)
[920]1007        d = {'b': scan.getbeam, 's': scan.getscan,
[1949]1008             'i': scan.getif, 'p': scan.getpol, 't': scan.get_time,
[1989]1009             'r': int}#, '_r': int}
[920]1010
[2650]1011        polmodes = dict(zip(sel.get_pols(), sel.get_poltypes()))
[1148]1012        # this returns either a tuple of numbers or a length  (ncycles)
1013        # convert this into lengths
1014        n0,nstack0 = self._get_selected_n(scan)
1015        if isinstance(n0, int): n = n0
[1175]1016        else: n = len(n0)
[1148]1017        if isinstance(nstack0, int): nstack = nstack0
[1175]1018        else: nstack = len(nstack0)
[1989]1019        # In case of row stacking
1020        rowstack = False
1021        titlemode = self._panelling
1022        if self._stacking == "r" and self._panelling != "r":
1023            rowstack = True
1024            titlemode = '_r'
[1913]1025        nptot = n
[1582]1026        maxpanel, maxstack = 16,16
[1913]1027        if nstack > maxstack:
1028            msg ="Scan to be overlayed contains more than %d selections.\n" \
1029                  "Selecting first %d selections..." % (maxstack, maxstack)
[920]1030            asaplog.push(msg)
[1861]1031            asaplog.post('WARN')
[998]1032            nstack = min(nstack,maxstack)
[2038]1033        #n = min(n-self._ipanel-1,maxpanel)
1034        n = n-self._ipanel-1
[2697]1035        # the number of panels in this page
1036        if self._rows and self._cols:
1037            n = min(n,self._rows*self._cols)
1038        else:
1039            n = min(n,maxpanel)
[2011]1040
[2697]1041        firstpage = (self._ipanel < 0)
1042        #ganged = False
1043        ganged = rcParams['plotter.ganged']
1044        if self._panelling == 'i':
1045            ganged = False
[2715]1046        if (not firstpage) and \
1047               self._plotter._subplotsOk(self._rows, self._cols, n):
1048            # Not the first page and subplot number is ok.
1049            # Just clear the axis
[2697]1050            nx = self._plotter.cols
1051            ipaxx = n - nx - 1 #the max panel id to supress x-label
1052            for ip in xrange(len(self._plotter.subplots)):
1053                self._plotter.subplot(ip)
1054                self._plotter.clear()
1055                self._plotter.axes.set_visible((ip<n))
1056                if ganged:
1057                    self._plotter.axes.xaxis.label.set_visible((ip > ipaxx))
1058                    if ip <= ipaxx:
1059                        map(lambda x: x.set_visible(False), \
1060                            self._plotter.axes.get_xticklabels())
1061                    self._plotter.axes.yaxis.label.set_visible((ip % nx)==0)
1062                    if ip % nx:
1063                        map(lambda y: y.set_visible(False), \
1064                            self._plotter.axes.get_yticklabels())
1065        elif (n > 1 and self._rows and self._cols):
[920]1066                self._plotter.set_panels(rows=self._rows,cols=self._cols,
[2650]1067                                         nplots=n,margin=self._margins,
1068                                         ganged=ganged)
[920]1069        else:
[2697]1070            self._plotter.set_panels(rows=n,cols=0,nplots=n,
1071                                     margin=self._margins,ganged=ganged)
[1913]1072        #r = 0
[1981]1073        r = self._startrow
[920]1074        nr = scan.nrow()
1075        a0,b0 = -1,-1
1076        allxlim = []
[1018]1077        allylim = []
[1981]1078        #newpanel=True
1079        newpanel=False
[920]1080        panelcount,stackcount = 0,0
[1981]1081        # If this is not the first page
1082        if r > 0:
1083            # panelling value of the prev page
1084            a0 = d[self._panelling](r-1)
1085            # set the initial stackcount large not to plot
1086            # the start row automatically
1087            stackcount = nstack
1088
[1002]1089        while r < nr:
[920]1090            a = d[self._panelling](r)
1091            b = d[self._stacking](r)
1092            if a > a0 and panelcount < n:
1093                if n > 1:
1094                    self._plotter.subplot(panelcount)
1095                self._plotter.palette(0)
1096                #title
1097                xlab = self._abcissa and self._abcissa[panelcount] \
1098                       or scan._getabcissalabel()
[1897]1099                if self._offset and not self._abcissa:
1100                    xlab += " (relative)"
[920]1101                ylab = self._ordinate and self._ordinate[panelcount] \
1102                       or scan._get_ordinate_label()
[1547]1103                self._plotter.set_axes('xlabel', xlab)
1104                self._plotter.set_axes('ylabel', ylab)
[1989]1105                #lbl = self._get_label(scan, r, self._panelling, self._title)
1106                lbl = self._get_label(scan, r, titlemode, self._title)
[920]1107                if isinstance(lbl, list) or isinstance(lbl, tuple):
1108                    if 0 <= panelcount < len(lbl):
1109                        lbl = lbl[panelcount]
1110                    else:
1111                        # get default label
[1989]1112                        #lbl = self._get_label(scan, r, self._panelling, None)
1113                        lbl = self._get_label(scan, r, titlemode, None)
[920]1114                self._plotter.set_axes('title',lbl)
1115                newpanel = True
[1913]1116                stackcount = 0
[920]1117                panelcount += 1
[1981]1118                # save the start row to plot this panel for future revisit.
1119                if self._panelling != 'r' and \
1120                       len(self._panelrows) < self._ipanel+1+panelcount:
1121                    self._panelrows += [r]
1122                   
[1944]1123            #if (b > b0 or newpanel) and stackcount < nstack:
[2650]1124            if stackcount < nstack and (newpanel or \
1125                                            rowstack or (a == a0 and b > b0)):
[920]1126                y = []
1127                if len(polmodes):
1128                    y = scan._getspectrum(r, polmodes[scan.getpol(r)])
1129                else:
1130                    y = scan._getspectrum(r)
[1995]1131                # flag application
1132                mr = scan._getflagrow(r)
[1739]1133                from numpy import ma, array
[1995]1134                if mr:
1135                    y = ma.masked_array(y,mask=mr)
1136                else:
1137                    m = scan._getmask(r)
1138                    from numpy import logical_not, logical_and
1139                    if self._maskselection and len(self._usermask) == len(m):
[2277]1140                        if d[self._stacking](r) in self._maskselection[self._stacking]:
[1995]1141                            m = logical_and(m, self._usermask)
[2277]1142                    y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
[1995]1143
[1897]1144                x = array(scan._getabcissa(r))
1145                if self._offset:
1146                    x += self._offset
[920]1147                if self._minmaxx is not None:
1148                    s,e = self._slice_indeces(x)
1149                    x = x[s:e]
1150                    y = y[s:e]
[1096]1151                if len(x) > 1024 and rcParams['plotter.decimate']:
1152                    fac = len(x)/1024
[920]1153                    x = x[::fac]
1154                    y = y[::fac]
1155                llbl = self._get_label(scan, r, self._stacking, self._lmap)
1156                if isinstance(llbl, list) or isinstance(llbl, tuple):
1157                    if 0 <= stackcount < len(llbl):
1158                        # use user label
1159                        llbl = llbl[stackcount]
1160                    else:
1161                        # get default label
1162                        llbl = self._get_label(scan, r, self._stacking, None)
1163                self._plotter.set_line(label=llbl)
[1023]1164                plotit = self._plotter.plot
1165                if self._hist: plotit = self._plotter.hist
[1995]1166                if len(x) > 0 and not mr:
[1146]1167                    plotit(x,y)
1168                    xlim= self._minmaxx or [min(x),max(x)]
1169                    allxlim += xlim
1170                    ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
1171                    allylim += ylim
[1819]1172                else:
1173                    xlim = self._minmaxx or []
1174                    allxlim += xlim
1175                    ylim= self._minmaxy or []
1176                    allylim += ylim
[920]1177                stackcount += 1
[1981]1178                a0=a
1179                b0=b
[920]1180                # last in colour stack -> autoscale x
[1819]1181                if stackcount == nstack and len(allxlim) > 0:
[920]1182                    allxlim.sort()
[1819]1183                    self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
[1989]1184                    if ganged:
1185                        allxlim = [allxlim[0],allxlim[-1]]
1186                    else:
1187                        # clear
1188                        allxlim =[]
[920]1189
1190            newpanel = False
[1981]1191            #a0=a
1192            #b0=b
[920]1193            # ignore following rows
[1981]1194            if (panelcount == n and stackcount == nstack) or (r == nr-1):
[1018]1195                # last panel -> autoscale y if ganged
[1989]1196                #if rcParams['plotter.ganged'] and len(allylim) > 0:
1197                if ganged and len(allylim) > 0:
[1018]1198                    allylim.sort()
1199                    self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
[998]1200                break
[920]1201            r+=1 # next row
[1981]1202
1203        # save the current counter for multi-page plotting
1204        self._startrow = r+1
1205        self._ipanel += panelcount
[2147]1206        if self.casabar_exists():
[1981]1207            if self._ipanel >= nptot-1:
[1913]1208                self._plotter.figmgr.casabar.disable_next()
1209            else:
1210                self._plotter.figmgr.casabar.enable_next()
[1981]1211            if self._ipanel + 1 - panelcount > 0:
1212                self._plotter.figmgr.casabar.enable_prev()
1213            else:
1214                self._plotter.figmgr.casabar.disable_prev()
1215
[947]1216        #reset the selector to the scantable's original
1217        scan.set_selection(savesel)
[1824]1218
[1819]1219        #temporary switch-off for older matplotlib
1220        #if self._fp is not None:
[2698]1221        if self._fp is not None and \
1222               getattr(self._plotter.figure,'findobj',False):
[1556]1223            for o in self._plotter.figure.findobj(Text):
[2697]1224                if not self._headtext['textobj'] or \
1225                   not (o in self._headtext['textobj']):
1226                    o.set_fontproperties(self._fp)
[920]1227
[1910]1228    def _get_sortstring(self, lorders):
1229        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
1230              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME', 'r':None, '_r':None }
[1944]1231        if not (type(lorders) == list) and not (type(lorders) == tuple):
[1910]1232            return None
1233        if len(lorders) > 0:
1234            lsorts = []
1235            for order in lorders:
[1989]1236                if order == "r":
1237                    # don't sort if row panelling/stacking
1238                    return None
[1910]1239                ssort = d0[order]
1240                if ssort:
1241                    lsorts.append(ssort)
1242            return lsorts
1243        return None
1244
[920]1245    def _get_selected_n(self, scan):
[1148]1246        d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
[1910]1247             'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle,
[1989]1248             'r': scan.nrow}#, '_r': False}
[1148]1249        d2 = { 'b': self._selection.get_beams(),
1250               's': self._selection.get_scans(),
1251               'i': self._selection.get_ifs(),
1252               'p': self._selection.get_pols(),
[1910]1253               't': self._selection.get_cycles(),
[1989]1254               'r': False}#, '_r': 1}
[920]1255        n =  d2[self._panelling] or d1[self._panelling]()
1256        nstack = d2[self._stacking] or d1[self._stacking]()
[1989]1257        # handle row panelling/stacking
1258        if self._panelling == 'r':
1259            nstack = 1
1260        elif self._stacking == 'r':
1261            n = 1
[920]1262        return n,nstack
1263
1264    def _get_label(self, scan, row, mode, userlabel=None):
[1153]1265        if isinstance(userlabel, list) and len(userlabel) == 0:
1266            userlabel = " "
[947]1267        pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
[920]1268        if len(pms):
1269            poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
1270        else:
1271            poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
1272        d = {'b': "Beam "+str(scan.getbeam(row)),
[1819]1273             #'s': scan._getsourcename(row),
1274             's': "Scan "+str(scan.getscan(row))+\
1275                  " ("+str(scan._getsourcename(row))+")",
[920]1276             'i': "IF"+str(scan.getif(row)),
[964]1277             'p': poleval,
[1910]1278             't': str(scan.get_time(row)),
1279             'r': "row "+str(row),
[1913]1280             #'_r': str(scan.get_time(row))+",\nIF"+str(scan.getif(row))+", "+poleval+", Beam"+str(scan.getbeam(row)) }
1281             '_r': "" }
[920]1282        return userlabel or d[mode]
[1153]1283
[2700]1284    def _slice_indeces(self, data):
1285        mn = self._minmaxx[0]
1286        mx = self._minmaxx[1]
1287        asc = data[0] < data[-1]
1288        start=0
1289        end = len(data)-1
1290        inc = 1
1291        if not asc:
1292            start = len(data)-1
1293            end = 0
1294            inc = -1
1295        # find min index
1296        #while start > 0 and data[start] < mn:
1297        #    start+= inc
1298        minind=start
1299        for ind in xrange(start,end+inc,inc):
1300            if data[ind] > mn: break
1301            minind=ind
1302        # find max index
1303        #while end > 0 and data[end] > mx:
1304        #    end-=inc
1305        #if end > 0: end +=1
1306        maxind=end
1307        for ind in xrange(end,start-inc,-inc):
1308            if data[ind] < mx: break
1309            maxind=ind
1310        start=minind
1311        end=maxind
1312        if start > end:
1313            return end,start+1
1314        elif start < end:
1315            return start,end+1
1316        else:
1317            return start,end
1318
[2988]1319    def _get_date_axis_setup(self, dates):
1320        """
1321        Returns proper axis title and formatters for a list of dates
1322        Input
1323            dates : a list of datetime objects returned by,
1324                    e.g. scantable.get_time(asdatetime=True)
1325        Output
1326            a set of
1327            * date axis title string
1328            * formatter of date axis
1329            * major axis locator
1330            * minor axis locator
1331        """
1332        from matplotlib import pylab as PL
1333        from matplotlib.dates import DateFormatter
1334        from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
1335        t = PL.date2num(dates)
1336        tdel = max(t) - min(t) # interval in day
1337        dstr = dates[0].strftime('%Y/%m/%d')
1338        if tdel > 1.0: # >1day
1339            dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
1340            dstr = dstr + " - " + dstr2
1341            majloc = DayLocator()
1342            minloc = HourLocator(range(0,23,12))
1343            timefmt = DateFormatter("%b%d")
1344        elif tdel > 24./60.: # 9.6h - 1day
1345            timefmt = DateFormatter('%H:%M')
1346            majloc = HourLocator()
1347            minloc = MinuteLocator(range(0,60,30))
1348        elif tdel > 2./24.: # 2h-9.6h
1349            timefmt = DateFormatter('%H:%M')
1350            majloc = HourLocator()
1351            minloc = MinuteLocator(range(0,60,10))
1352        elif tdel> 10./24./60.: # 10min-2h
1353            timefmt = DateFormatter('%H:%M')
1354            majloc = MinuteLocator(range(0,60,10))
1355            minloc = MinuteLocator()
1356        else: # <10min
1357            timefmt = DateFormatter('%H:%M')
1358            majloc = MinuteLocator()
1359            minloc = SecondLocator(30)
1360        return (dstr, timefmt, majloc, minloc)
1361
[1819]1362    def plotazel(self, scan=None, outfile=None):
[1391]1363        """
[1696]1364        plot azimuth and elevation versus time of a scantable
[1391]1365        """
[2704]1366        self._plotmode = "azel"
[1923]1367        visible = rcParams['plotter.gui']
[1696]1368        from matplotlib import pylab as PL
[2586]1369        from pytz import timezone
[1391]1370        from matplotlib.ticker import MultipleLocator
[2953]1371        from numpy import array, pi, ma
[2704]1372        if self._plotter and (PL.gcf() == self._plotter.figure):
[2699]1373            # the current figure is ASAP plotter. Use mpl plotter
1374            figids = PL.get_fignums()
[2704]1375            PL.figure(max(figids[-1],1))
1376
[1923]1377        if not visible or not self._visible:
1378            PL.ioff()
1379            from matplotlib.backends.backend_agg import FigureCanvasAgg
1380            PL.gcf().canvas.switch_backends(FigureCanvasAgg)
[1819]1381        self._data = scan
[1556]1382        dates = self._data.get_time(asdatetime=True)
[2953]1383        # for flag handling
1384        mask = [ self._data._is_all_chan_flagged(i) for i in range(self._data.nrow())]
[1391]1385        t = PL.date2num(dates)
1386        tz = timezone('UTC')
1387        PL.cla()
1388        PL.ioff()
1389        PL.clf()
[2037]1390        # Adjust subplot margins
[2576]1391        if not self._margins or len(self._margins) != 6:
[2037]1392            self.set_margin(refresh=False)
1393        lef, bot, rig, top, wsp, hsp = self._margins
[1819]1394        PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1395                                 wspace=wsp,hspace=hsp)
[1824]1396
[2977]1397        tdel = max(t) - min(t) # interval in day
[1391]1398        ax = PL.subplot(2,1,1)
[2953]1399        el = ma.masked_array(array(self._data.get_elevation())*180./pi, mask)
[1391]1400        PL.ylabel('El [deg.]')
[2988]1401        (dstr, timefmt, majloc, minloc) = self._get_date_axis_setup(dates)
1402       
[1391]1403        PL.title(dstr)
[1819]1404        if tdel == 0.0:
1405            th = (t - PL.floor(t))*24.0
1406            PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
1407        else:
1408            PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
[3016]1409            #ax.xaxis.set_major_formatter(timefmt)
1410            #ax.xaxis.set_major_locator(majloc)
1411            #ax.xaxis.set_minor_locator(minloc)
[1391]1412        ax.yaxis.grid(True)
[1819]1413        ax.set_ylim(0,90)
[3016]1414        #yloc = MultipleLocator(30)
1415        #ax.yaxis.set_major_locator(yloc)
[1391]1416        if tdel > 1.0:
1417            labels = ax.get_xticklabels()
1418        #    PL.setp(labels, fontsize=10, rotation=45)
1419            PL.setp(labels, fontsize=10)
[1819]1420
[1391]1421        # Az plot
[2953]1422        az = ma.masked_array(array(self._data.get_azimuth())*180./pi, mask)
[1391]1423        if min(az) < 0:
1424            for irow in range(len(az)):
1425                if az[irow] < 0: az[irow] += 360.0
1426
[1819]1427        ax2 = PL.subplot(2,1,2)
1428        #PL.xlabel('Time (UT [hour])')
1429        PL.ylabel('Az [deg.]')
1430        if tdel == 0.0:
1431            PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
1432        else:
1433            PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
[3016]1434            #ax2.xaxis.set_major_formatter(timefmt)
1435            #ax2.xaxis.set_major_locator(majloc)
1436            #ax2.xaxis.set_minor_locator(minloc)
[1819]1437        ax2.set_ylim(0,360)
[1696]1438        ax2.yaxis.grid(True)
[1819]1439        #hfmt = DateFormatter('%H')
1440        #hloc = HourLocator()
[3016]1441        #yloc = MultipleLocator(60)
1442        #ax2.yaxis.set_major_locator(yloc)
[1819]1443        if tdel > 1.0:
1444            labels = ax2.get_xticklabels()
1445            PL.setp(labels, fontsize=10)
[3016]1446        #    PL.xlabel('Time (UT [day])')
1447        #else:
1448        #    PL.xlabel('Time (UT [hour])')
1449        PL.xlabel('Time (UT)')
[1819]1450
[1391]1451        PL.ion()
1452        PL.draw()
[2416]1453        if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
[2576]1454        if (outfile is not None):
1455           PL.savefig(outfile)
[1391]1456
[2693]1457
1458    def plotpointing2(self, scan=None, colorby='', showline=False, projection=''):
1459        """
1460        plot telescope pointings
1461        Parameters:
[2990]1462            scan    : input scantable instance
[2693]1463            colorby : change color by either
1464                      'type'(source type)|'scan'|'if'|'pol'|'beam'
1465            showline : show dotted line
1466            projection : projection type either
1467                         ''(no projection [deg])|'coord'(not implemented)
1468        """
[2704]1469        self._plotmode = "pointing"
[2953]1470        from numpy import array, pi, ma
[2693]1471        from asap import scantable
1472        # check for scantable
1473        if isinstance(scan, scantable):
1474            if self._data is not None:
1475                if scan != self._data:
1476                    self._data = scan
1477                    # reset
1478                    self._reset()
1479            else:
1480                self._data = scan
1481                self._reset()
1482        if not self._data:
1483            msg = "Input is not a scantable"
1484            raise TypeError(msg)
1485        # check for color mode
1486        validtypes=['type','scan','if','pol', 'beam']
1487        stype = None
1488        if (colorby in validtypes):
1489            stype = colorby[0]
1490        elif len(colorby) > 0:
1491            msg = "Invalid choice of 'colorby' (choices: %s)" % str(validtypes)
1492            raise ValueError(msg)
1493        self._assert_plotter(action="reload")
1494        self._plotter.hold()
[2697]1495        self._reset_counter()
[2694]1496        if self.casabar_exists():
1497            self._plotter.figmgr.casabar.disable_button()
[2693]1498        # for now, only one plot
1499        self._plotter.set_panels(rows=1,cols=1)
1500        # first panel
1501        self._plotter.subplot(0)
1502        # first color and linestyles
1503        self._plotter.palette(0)
1504        self.gca().set_aspect('equal')
1505        basesel = scan.get_selection()
[2694]1506        attrback = self._plotter.get_line()
1507        marker = "o"
[2693]1508        if showline:
1509            basesel.set_order(["TIME"])
1510            scan.set_selection(basesel)
1511            if not (stype in ["t", "s"]):
[2694]1512                marker += ":"
1513        self._plotter.set_line(markersize=3, markeredgewidth=0)
1514
[2693]1515        if not stype:
1516            selIds = [""] # cheating
1517            sellab = "all points"
1518        elif stype == 't':
1519            selIds = range(15)
1520            sellab = "src type "
1521        else:
1522            selIds = getattr(self._data,'get'+colorby+'nos')()
1523            sellab = colorby.upper()
1524        selFunc = "set_"+colorby+"s"
1525        for idx in selIds:
1526            sel = selector() + basesel
1527            if stype:
1528                bid = getattr(basesel,'get_'+colorby+"s")()
1529                if (len(bid) > 0) and (not idx in bid):
1530                    # base selection doesn't contain idx
1531                    # Note summation of selector is logical sum if
1532                    continue
1533                getattr(sel, selFunc)([idx])
1534            if not sel.is_empty():
1535                try:
1536                    self._data.set_selection(sel)
1537                except RuntimeError, instance:
1538                    if stype == 't' and str(instance).startswith("Selection contains no data."):
1539                        continue
1540                    else:
1541                        self._data.set_selection(basesel)
1542                        raise RuntimeError, instance
1543            if self._data.nrow() == 0:
1544                self._data.set_selection(basesel)
1545                continue
[2795]1546            #print "Plotting direction of %s = %s" % (colorby, str(idx))
[2694]1547            # getting data to plot
[2693]1548            dir = array(self._data.get_directionval()).transpose()
[2953]1549            # for flag handling
1550            mask = [ self._data._is_all_chan_flagged(i) for i in range(self._data.nrow())]
[2693]1551            ra = dir[0]*180./pi
[2953]1552            dec = ma.masked_array(dir[1]*180./pi, mask)
[2694]1553            # actual plot
[2693]1554            self._plotter.set_line(label=(sellab+str(idx)))
1555            self._plotter.plot(ra,dec,marker)
1556
1557        # restore original selection
1558        self._data.set_selection(basesel)
1559        # need to plot scan pattern explicitly
1560        if showline and (stype in ["t", "s"]):
1561            dir = array(self._data.get_directionval()).transpose()
1562            ra = dir[0]*180./pi
1563            dec = dir[1]*180./pi
1564            self._plotter.set_line(label="scan pattern")
1565            self._plotter.plot(ra,dec,":")
[2694]1566            # set color for only this line
1567            self._plotter.lines[-1][0].set_color("gray")
1568
[2693]1569        xlab = 'RA [deg.]'
1570        ylab = 'Declination [deg.]'
1571        self._plotter.set_axes('xlabel', xlab)
1572        self._plotter.set_axes('ylabel', ylab)
1573        self._plotter.set_axes('title', 'Telescope pointings')
1574        if stype: self._plotter.legend(self._legendloc)
1575        else: self._plotter.legend(None)
1576        # reverse x-axis
1577        xmin, xmax = self.gca().get_xlim()
[2989]1578        ymin, ymax = self.gca().get_ylim()
1579        # expand plotrange if xmin==xmax or ymin==ymax
1580        if abs(ymax-ymin) < 1.e-3: #~4arcsec
1581            delx = 0.5*abs(xmax - xmin)
1582            if delx < 5.e-4:
1583                dxy = 5.e-4 #~2arcsec
1584                (ymin, ymax) = (ymin-dxy, ymax+dxy)
1585                (xmin, xmax) = (xmin-dxy, xmax+dxy)
1586            (ymin, ymax) = (ymin-delx, ymax+delx)
1587        elif abs(xmax-xmin) < 1.e-3:
1588            dely = 0.5*abs(ymax - ymin)
1589            (xmin, xmax) = (xmin-dely, xmax+dely)
1590        self._plotter.set_limits(xlim=[xmax,xmin], ylim=[ymin, ymax])
[2693]1591
1592        self._plotter.release()
1593        self._plotter.show(hardrefresh=False)
[2694]1594        # reset line settings
1595        self._plotter.set_line(**attrback)
[2693]1596        return
1597
[1819]1598    def plotpointing(self, scan=None, outfile=None):
[1391]1599        """
1600        plot telescope pointings
1601        """
[1923]1602        visible = rcParams['plotter.gui']
[1696]1603        from matplotlib import pylab as PL
[1819]1604        from numpy import array, pi
[2704]1605        if self._plotter and (PL.gcf() == self._plotter.figure):
[2699]1606            # the current figure is ASAP plotter. Use mpl plotter
1607            figids = PL.get_fignums()
[2704]1608            PL.figure(max(figids[-1],1))
1609
[1923]1610        if not visible or not self._visible:
1611            PL.ioff()
1612            from matplotlib.backends.backend_agg import FigureCanvasAgg
1613            PL.gcf().canvas.switch_backends(FigureCanvasAgg)
[1819]1614        self._data = scan
[1391]1615        dir = array(self._data.get_directionval()).transpose()
1616        ra = dir[0]*180./pi
1617        dec = dir[1]*180./pi
1618        PL.cla()
[1819]1619        #PL.ioff()
[1391]1620        PL.clf()
[2037]1621        # Adjust subplot margins
[2576]1622        if not self._margins or len(self._margins) != 6:
[2037]1623            self.set_margin(refresh=False)
1624        lef, bot, rig, top, wsp, hsp = self._margins
[1819]1625        PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1626                                 wspace=wsp,hspace=hsp)
1627        ax = PL.gca()
1628        #ax = PL.axes([0.1,0.1,0.8,0.8])
1629        #ax = PL.axes([0.1,0.1,0.8,0.8])
[1391]1630        ax.set_aspect('equal')
[1696]1631        PL.plot(ra, dec, 'b,')
[1391]1632        PL.xlabel('RA [deg.]')
1633        PL.ylabel('Declination [deg.]')
1634        PL.title('Telescope pointings')
1635        [xmin,xmax,ymin,ymax] = PL.axis()
1636        PL.axis([xmax,xmin,ymin,ymax])
[2416]1637        PL.ion()
[1391]1638        PL.draw()
[2416]1639        if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
[2576]1640        if (outfile is not None):
1641           PL.savefig(outfile)
[1819]1642
1643    # plot total power data
1644    # plotting in time is not yet implemented..
[1862]1645    @asaplog_post_dec
[2990]1646    def plottp(self, scan=None, colorby=''):
1647        """
1648        Plot averaged spectra (total power) in time or in row ID (colorby='')
1649        Parameters:
1650            scan    : input scantable instance
1651            colorby : change color by either
1652                      'type'(source type)|'scan'|'if'|'pol'|'beam'|''
1653        """
[2704]1654        self._plotmode = "totalpower"
[1819]1655        from asap import scantable
1656        if not self._data and not scan:
1657            msg = "Input is not a scantable"
1658            raise TypeError(msg)
1659        if isinstance(scan, scantable):
1660            if self._data is not None:
1661                if scan != self._data:
1662                    self._data = scan
1663                    # reset
1664                    self._reset()
1665            else:
1666                self._data = scan
1667                self._reset()
1668        # ranges become invalid when abcissa changes?
1669        #if self._abcunit and self._abcunit != self._data.get_unit():
1670        #    self._minmaxx = None
1671        #    self._minmaxy = None
1672        #    self._abcunit = self._data.get_unit()
1673
[2693]1674        self._assert_plotter(action="reload")
1675        self._plotter.hold()
1676        self._plotter.clear()
[2037]1677        # Adjust subplot margins
[2576]1678        if not self._margins or len(self._margins) !=6:
1679            self.set_margin(refresh=False)
[2037]1680        lef, bot, rig, top, wsp, hsp = self._margins
[1819]1681        self._plotter.figure.subplots_adjust(
1682            left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
[2147]1683        if self.casabar_exists(): self._plotter.figmgr.casabar.disable_button()
[2990]1684        if len(colorby) == 0:
1685            self._plottp(self._data)
1686        else:
[2991]1687            self._plottp_in_time(self._data,colorby)
[1819]1688        if self._minmaxy is not None:
1689            self._plotter.set_limits(ylim=self._minmaxy)
1690        self._plotter.release()
1691        self._plotter.tidy()
1692        self._plotter.show(hardrefresh=False)
1693        return
1694
[2991]1695    def _plottp_in_time(self,scan,colorby):
[2990]1696        """
1697        private method for plotting total power data in time
[2991]1698        Parameters:
1699            scan    : input scantable instance
1700            colorby : change color by either
1701                      'type'(source type)|'scan'|'if'|'pol'|'beam'
[2990]1702        """
1703        from numpy import ma, array, arange, logical_not
1704        r=0
1705        nr = scan.nrow()
1706        a0,b0 = -1,-1
1707        allxlim = []
1708        allylim = []
1709        y=[]
1710        self._plotter.set_panels()
1711        self._plotter.palette(0)
1712        # check of overlay settings
[2992]1713        time_types = ['type','scan'] # time dependent meta-data
1714        misc_types = ['if','pol','beam'] # time independent meta-data
1715        validtypes=time_types + misc_types
[2990]1716        stype = None
[2991]1717        col_msg = "Invalid choice of 'colorby' (choices: %s)" % str(validtypes)
[2992]1718        colorby = colorby.lower()
[2990]1719        if (colorby in validtypes):
1720            stype = colorby[0]
1721        elif len(colorby) > 0:
[2991]1722            raise ValueError(col_msg)
[2990]1723        if not stype:
[2991]1724            raise ValueError(col_msg)
[2992]1725        # Selection and sort order
1726        basesel = scan.get_selection()
1727        if colorby in misc_types: misc_types.pop(misc_types.index(colorby))
1728        sel_lbl = ""
1729        for meta in misc_types:
1730            idx = getattr(scan,'get'+meta+'nos')()
1731            if len(idx) > 1: getattr(basesel, 'set_'+meta+'s')([idx[0]])
1732            sel_lbl += ("%s%d, " % (meta.upper(), idx[0]))
1733        sel_lbl = sel_lbl.rstrip(', ')
1734        scan.set_selection(basesel)
1735        if len(sel_lbl) > 0:
1736            asaplog.push("Selection contains multiple IFs/Pols/Beams. Plotting the first ones: %s" % sel_lbl)
1737            asaplog.post("WARN")
1738        if stype == 't':
[2990]1739            selIds = range(15)
1740            sellab = "src type "
1741        else:
1742            selIds = getattr(scan,'get'+colorby+'nos')()
1743            sellab = colorby.upper()
1744        selFunc = "set_"+colorby+"s"
[2992]1745        basesel.set_order(["TIME"])
1746        # define axes labels
1747        xlab = self._abcissa or 'Time (UTC)'
1748        ylab = self._ordinate or scan._get_ordinate_label()
1749        self._plotter.set_axes('xlabel',xlab)
1750        self._plotter.set_axes('ylabel',ylab)
1751        # define the panel title
1752        if len(sel_lbl) > 0: lbl = sel_lbl
1753        else: lbl = self._get_label(scan, r, 's', self._title)
1754        if isinstance(lbl, list) or isinstance(lbl, tuple):
1755            # get default label
1756            lbl = self._get_label(scan, r, self._panelling, None)
1757        self._plotter.set_axes('title',lbl)
1758        # linestyle
1759        lstyle = '' if colorby in time_types else ':'
[2990]1760        alldates = []
1761        for idx in selIds:
1762            sel = selector() + basesel
[2991]1763            bid = getattr(basesel,'get_'+colorby+"s")()
1764            if (len(bid) > 0) and (not idx in bid):
1765                # base selection doesn't contain idx
1766                # Note summation of selector is logical sum if
1767                continue
1768            getattr(sel, selFunc)([idx])
[2990]1769            if not sel.is_empty():
1770                try:
1771                    scan.set_selection(sel)
1772                except RuntimeError, instance:
1773                    if stype == 't' and str(instance).startswith("Selection contains no data."):
1774                        continue
1775                    else:
1776                        scan.set_selection(basesel)
1777                        raise RuntimeError, instance
1778            if scan.nrow() == 0:
1779                scan.set_selection(basesel)
1780                continue
1781            y=array(scan._get_column(scan._getspectrum,-1))
1782            m = array(scan._get_column(scan._getmask,-1))
1783            y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1784            # try to handle spectral data somewhat...
[2991]1785            try:
1786                l,m = y.shape
1787            except ValueError, e:
[2992]1788                raise ValueError(str(e)+" This error usually occurs when you select multiple spws with different number of channels. Try selecting single spw and retry.")
[2990]1789            if m > 1:
1790                y=y.mean(axis=1)
1791            # flag handling
1792            m = [ scan._is_all_chan_flagged(i) for i in range(scan.nrow()) ]
1793            y = ma.masked_array(y,mask=m)
1794            if len(y) == 0: continue
[2992]1795            # line label
1796            llbl=sellab+str(idx)
[2991]1797            from matplotlib.dates import date2num
1798            from pytz import timezone
1799            dates = self._data.get_time(asdatetime=True)
1800            alldates += list(dates)
1801            x = date2num(dates)
1802            tz = timezone('UTC')
[2992]1803            # get color
1804            lc = self._plotter.colormap[self._plotter.color]
1805            self._plotter.palette( (self._plotter.color+1) % len(self._plotter.colormap) )
1806            # actual plotting
1807            self._plotter.axes.plot_date(x,y,tz=tz,label=llbl,linestyle=lstyle,color=lc,
1808                                         marker='o',markersize=3,markeredgewidth=0)
[2991]1809
[2990]1810        # legend and axis formatting
[2991]1811        (dstr, timefmt, majloc, minloc) = self._get_date_axis_setup(alldates)
1812        ax = self.gca()
1813        ax.xaxis.set_major_formatter(timefmt)
1814        ax.xaxis.set_major_locator(majloc)
1815        ax.xaxis.set_minor_locator(minloc)
[2992]1816        self._plotter.axes.legend(loc=self._legendloc)
[2990]1817
[1819]1818    def _plottp(self,scan):
1819        """
1820        private method for plotting total power data
1821        """
1822        from numpy import ma, array, arange, logical_not
1823        r=0
1824        nr = scan.nrow()
1825        a0,b0 = -1,-1
1826        allxlim = []
1827        allylim = []
1828        y=[]
1829        self._plotter.set_panels()
1830        self._plotter.palette(0)
1831        #title
1832        #xlab = self._abcissa and self._abcissa[panelcount] \
1833        #       or scan._getabcissalabel()
1834        #ylab = self._ordinate and self._ordinate[panelcount] \
1835        #       or scan._get_ordinate_label()
1836        xlab = self._abcissa or 'row number' #or Time
1837        ylab = self._ordinate or scan._get_ordinate_label()
1838        self._plotter.set_axes('xlabel',xlab)
1839        self._plotter.set_axes('ylabel',ylab)
1840        lbl = self._get_label(scan, r, 's', self._title)
1841        if isinstance(lbl, list) or isinstance(lbl, tuple):
1842        #    if 0 <= panelcount < len(lbl):
1843        #        lbl = lbl[panelcount]
1844        #    else:
1845                # get default label
1846             lbl = self._get_label(scan, r, self._panelling, None)
1847        self._plotter.set_axes('title',lbl)
1848        y=array(scan._get_column(scan._getspectrum,-1))
1849        m = array(scan._get_column(scan._getmask,-1))
1850        y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1851        x = arange(len(y))
1852        # try to handle spectral data somewhat...
[2992]1853        try:
1854            l,m = y.shape
1855        except ValueError, e:
1856                raise ValueError(str(e)+" This error usually occurs when you select multiple spws with different number of channels. Try selecting single spw and retry.")
[1819]1857        if m > 1:
1858            y=y.mean(axis=1)
[2953]1859        # flag handling
1860        m = [ scan._is_all_chan_flagged(i) for i in range(scan.nrow()) ]
1861        y = ma.masked_array(y,mask=m)
[1819]1862        plotit = self._plotter.plot
1863        llbl = self._get_label(scan, r, self._stacking, None)
1864        self._plotter.set_line(label=llbl)
1865        if len(x) > 0:
1866            plotit(x,y)
1867
1868
1869    # printing header information
[1862]1870    @asaplog_post_dec
[2053]1871    def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
[1819]1872        """
1873        print data (scantable) header on the plot and/or logger.
[2056]1874        To plot the header on the plot, this method should be called after
1875        plotting spectra by the method, asapplotter.plot.
[1819]1876        Parameters:
[1824]1877            plot:      whether or not print header info on the plot.
[2053]1878            fontsize:  header font size (valid only plot=True)
[1819]1879            logger:    whether or not print header info on the logger.
1880            selstr:    additional selection string (not verified)
[2053]1881            extrastr:  additional string to print at the beginning (not verified)
[1819]1882        """
[1859]1883        if not plot and not logger:
1884            return
1885        if not self._data:
1886            raise RuntimeError("No scantable has been set yet.")
[1824]1887        # Now header will be printed on plot and/or logger.
1888        # Get header information and format it.
[2112]1889        ssum=self._data._list_header()
[1819]1890        # Print Observation header to the upper-left corner of plot
[2290]1891        headstr=[ssum[0:ssum.find('Obs. Type:')]]
1892        headstr.append(ssum[ssum.find('Obs. Type:'):ssum.find('Flux Unit:')])
[2053]1893        if extrastr != '':
1894            headstr[0]=extrastr+'\n'+headstr[0]
1895            self._headtext['extrastr'] = extrastr
[2112]1896        if selstr != '':
1897            selstr += '\n'
1898            self._headtext['selstr'] = selstr
[2944]1899        #ssel=(selstr+self._data.get_selection().__str__()+self._selection.__str__() or 'none')
[2945]1900        curr_selstr = selstr+self._data.get_selection().__str__() or "none"
1901        ssel=(curr_selstr+"\n" +self._selection.__str__())
[2927]1902        headstr.append('\n\n***Selections***\n'+ssel.replace('$','\$'))
[1824]1903
[2051]1904        if plot:
[2451]1905            errmsg = "Can plot header only after the first call to plot()."
[2453]1906            self._assert_plotter(action="halt",errmsg=errmsg)
[1819]1907            self._plotter.hold()
[2053]1908            self._header_plot(headstr,fontsize=fontsize)
[2697]1909            #import time
1910            #self._plotter.figure.text(0.99,0.01,
1911            #                time.strftime("%a %d %b %Y  %H:%M:%S %Z"),
1912            #                horizontalalignment='right',
1913            #                verticalalignment='bottom',fontsize=8)
[1819]1914            self._plotter.release()
1915        if logger:
[2053]1916            selstr = "Selections:    "+ssel
[1819]1917            asaplog.push("----------------\n  Plot Summary\n----------------")
[2053]1918            asaplog.push(extrastr)
[2290]1919            asaplog.push(ssum[0:ssum.find('Selection:')]\
[2112]1920                         + selstr)
[2053]1921        self._headtext['string'] = headstr
1922        del ssel, ssum, headstr
[2051]1923
[2053]1924    def _header_plot(self, texts, fontsize=9):
1925        self._headtext['textobj']=[]
1926        nstcol=len(texts)
1927        for i in range(nstcol):
1928            self._headtext['textobj'].append(
1929                self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1930                                          texts[i],
1931                                          horizontalalignment='left',
1932                                          verticalalignment='top',
1933                                          fontsize=fontsize))
1934
1935    def clear_header(self):
1936        if not self._headtext['textobj']:
1937            asaplog.push("No header has been plotted. Exit without any operation")
1938            asaplog.post("WARN")
[2453]1939        elif self._assert_plotter(action="status"):
[2053]1940            self._plotter.hold()
1941            for textobj in self._headtext['textobj']:
1942                #if textobj.get_text() in self._headstring:
1943                try:
1944                    textobj.remove()
1945                except NotImplementedError:
1946                    self._plotter.figure.texts.pop(self._plotter.figure.texts.index(textobj))
1947            self._plotter.release()
1948        self._reset_header()
[2576]1949
1950    # plot spectra by pointing
1951    @asaplog_post_dec
[2717]1952    def plotgrid(self, scan=None,center="",spacing=[],rows=None,cols=None):
[2576]1953        """
1954        Plot spectra based on direction.
1955       
1956        Parameters:
1957            scan:      a scantable to plot
[2717]1958            center:    the grid center direction (a string)
[2576]1959                       (default) the center of map region
[2717]1960                       (example) 'J2000 19h30m00s -25d00m00s'
[2576]1961            spacing:   a list of horizontal (R.A.) and vertical (Dec.)
[2717]1962                       spacing.
[2576]1963                       (default) Calculated by the extent of map region and
[2717]1964                       (example) ['1arcmin', '1arcmin']
[2576]1965                       the number of rows and cols to cover
1966            rows:      number of panels (grid points) in horizontal direction
1967            cols:      number of panels (grid points) in vertical direction
1968
1969        Note:
1970        - Only the first IFNO, POLNO, and BEAM in the scantable will be
1971        plotted.
1972        - This method doesn't re-grid and average spectra in scantable. Use
1973        asapgrid module to re-grid spectra before plotting with this method.
1974        Only the first spectrum is plotted in case there are multiple
1975        spectra which belong to a grid.
1976        """
[2704]1977        self._plotmode = "grid"
[2576]1978        from asap import scantable
[2607]1979        from numpy import array, ma, cos
[2576]1980        if not self._data and not scan:
1981            msg = "No scantable is specified to plot"
1982            raise TypeError(msg)
[2604]1983        if scan:
1984            self.set_data(scan, refresh=False)
1985            del scan
1986
[2576]1987        # Rows and cols
[2717]1988        if (self._rows is None):
1989            rows = max(1, rows)
1990        if (self._cols is None):
1991            cols = max(1, cols)
1992        self.set_layout(rows,cols,False)
[2576]1993
[2717]1994        # Select the first IF, POL, and BEAM for plotting
[2576]1995        ntotpl = self._rows * self._cols
1996        ifs = self._data.getifnos()
1997        if len(ifs) > 1:
1998            msg = "Found multiple IFs in scantable. Only the first IF (IFNO=%d) will be plotted." % ifs[0]
1999            asaplog.post()
2000            asaplog.push(msg)
2001            asaplog.post("WARN")
2002        pols = self._data.getpolnos()
2003        if len(pols) > 1:
2004            msg = "Found multiple POLs in scantable. Only the first POL (POLNO=%d) will be plotted." % pols[0]
2005            asaplog.post()
2006            asaplog.push(msg)
2007            asaplog.post("WARN")
2008        beams = self._data.getbeamnos()
2009        if len(beams) > 1:
2010            msg = "Found multiple BEAMs in scantable. Only the first BEAM (BEAMNO=%d) will be plotted." % beams[0]
2011            asaplog.post()
2012            asaplog.push(msg)
2013            asaplog.post("WARN")
2014        self._data.set_selection(ifs=[ifs[0]],pols=[pols[0]],beams=[beams[0]])
2015        if self._data.nrow() > ntotpl:
2016            msg = "Scantable is finely sampled than plotting grids. "\
2017                  + "Only the first spectrum is plotted in each grid."
2018            asaplog.post()
2019            asaplog.push(msg)
2020            asaplog.post("WARN")
[2717]2021
2022        # Prepare plotter
[2576]2023        self._assert_plotter(action="reload")
2024        self._plotter.hold()
[2697]2025        self._reset_counter()
[2604]2026        self._plotter.legend()
[2691]2027
[2576]2028        # Adjust subplot margins
2029        if not self._margins or len(self._margins) !=6:
2030            self.set_margin(refresh=False)
2031        self._plotter.set_panels(rows=self._rows,cols=self._cols,
[2693]2032                                 nplots=ntotpl,margin=self._margins,ganged=True)       
[2603]2033        if self.casabar_exists():
2034            self._plotter.figmgr.casabar.enable_button()
[2691]2035        # Plot helper
2036        from asap._asap import plothelper as plhelper
2037        ph = plhelper(self._data)
[2717]2038        #ph.set_gridval(self._cols, self._rows, spacing[0], spacing[1],
2039        #                  center[0], center[1], epoch="J2000", projname="SIN")
2040        if type(spacing) in (list, tuple, array):
2041            if len(spacing) == 0:
2042                spacing = ["", ""]
2043            elif len(spacing) == 1:
2044                spacing = [spacing[0], spacing[0]]
2045        else:
2046            spacing = [spacing, spacing]
2047        ph.set_grid(self._cols, self._rows, spacing[0], spacing[1], \
2048                    center, projname="SIN")
2049
[2576]2050        # Actual plot
2051        npl = 0
2052        for irow in range(self._data.nrow()):
[2691]2053            (ix, iy) = ph.get_gpos(irow)
2054            #print("asapplotter.plotgrid: (ix, iy) = (%f, %f)" % (ix, iy))
[2576]2055            if ix < 0 or ix >= self._cols:
[2602]2056                #print "Row %d : Out of X-range (x = %f) ... skipped" % (irow, pos[0])
[2576]2057                continue
[2691]2058            ix = int(ix)
2059            if iy < 0 or iy >= self._rows:
[2602]2060                #print "Row %d : Out of Y-range (y = %f) ... skipped" % (irow,pos[1])
[2576]2061                continue
[2691]2062            iy = int(iy)
2063            ipanel = ix + iy*self._rows
2064            #print("Resolved panel Id (%d, %d): %d" % (ix, iy, ipanel))
[2576]2065            if len(self._plotter.subplots[ipanel]['lines']) > 0:
[2602]2066                #print "Row %d : panel %d lready plotted ... skipped" % (irow,ipanel)
[2576]2067                # a spectrum already plotted in the panel
2068                continue
2069            # Plotting this row
[2602]2070            #print "PLOTTING row %d (panel=%d)" % (irow, ipanel)
[2576]2071            npl += 1
2072            self._plotter.subplot(ipanel)
[2602]2073            self._plotter.palette(0,colormap=self._colormap, \
2074                                  linestyle=0,linestyles=self._linestyles)
[2576]2075            xlab = self._abcissa and self._abcissa[ipanel] \
[2603]2076                   or self._data._getabcissalabel(irow)
[2576]2077            if self._offset and not self._abcissa:
2078                xlab += " (relative)"
2079            ylab = self._ordinate and self._ordinate[ipanel] \
[2603]2080                   or self._data._get_ordinate_label()
[2576]2081            self._plotter.set_axes('xlabel', xlab)
2082            self._plotter.set_axes('ylabel', ylab)
2083            lbl = self._data.get_direction(irow)
2084            self._plotter.set_axes('title',lbl)
2085
[2603]2086            y = self._data._getspectrum(irow)
[2576]2087            # flag application
[2603]2088            mr = self._data._getflagrow(irow)
[2576]2089            if mr:  # FLAGROW=True
2090                y = ma.masked_array(y,mask=mr)
2091            else:
[2603]2092                m = self._data._getmask(irow)
[2576]2093                from numpy import logical_not, logical_and
2094                ### user mask is not available so far
2095                #if self._maskselection and len(self._usermask) == len(m):
2096                #    if d[self._stacking](irow) in self._maskselection[self._stacking]:
2097                #            m = logical_and(m, self._usermask)
2098                y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
2099
[2603]2100            x = array(self._data._getabcissa(irow))
[2576]2101            if self._offset:
2102                x += self._offset
2103            if self._minmaxx is not None:
2104                s,e = self._slice_indeces(x)
2105                x = x[s:e]
2106                y = y[s:e]
2107            if len(x) > 1024 and rcParams['plotter.decimate']:
2108                fac = len(x)/1024
2109                x = x[::fac]
2110                y = y[::fac]
2111            self._plotter.set_line(label=lbl)
2112            plotit = self._plotter.plot
2113            if self._hist: plotit = self._plotter.hist
2114            if len(x) > 0 and not mr:
2115                plotit(x,y)
2116#                 xlim= self._minmaxx or [min(x),max(x)]
2117#                 allxlim += xlim
2118#                 ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
2119#                 allylim += ylim
2120#             else:
2121#                 xlim = self._minmaxx or []
2122#                 allxlim += xlim
2123#                 ylim= self._minmaxy or []
2124#                 allylim += ylim
2125           
2126            if npl >= ntotpl:
2127                break
2128           
2129        if self._minmaxy is not None:
2130            self._plotter.set_limits(ylim=self._minmaxy)
2131        self._plotter.release()
2132        self._plotter.tidy()
2133        self._plotter.show(hardrefresh=False)
2134        return
Note: See TracBrowser for help on using the repository browser.