source: trunk/python/asapplotter.py @ 2607

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

New Development: No (bug fixes)

JIRA Issue: Yes (CAS-1814/Trac-274)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: unit tests will be committed shortly (CASA)

Put in Release Notes: No

Module(s): asapplotter, sdplot

Description:

Take projection effect into account in plotting.
Fixed automatic calculation of spacing.


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