source: trunk/python/asapplotter.py @ 2451

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

New Development: No

JIRA Issue: Yes (CAS-3749)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: unit tests of sdplot

Put in Release Notes: No

Module(s): sdplot, sdfit, sdstat, sdflag, sdcal, sdreduce

Description:

Made asapplotter not to generate plotter window at start-up, but the window is
only generated at the first invokation of plotting operation.


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