source: trunk/python/asapplotter.py @ 2453

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

New Development: No

JIRA Issue: Yes (CAS-3749/Trac-266)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: renamed a parameter of asapplotter._assert_plotter from mode to action

Test Programs:

Put in Release Notes: No

Module(s):

Description: renamed a parameter of asapplotter._assert_plotter from mode to action


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