source: branches/alma/python/asapplotter.py @ 1724

Last change on this file since 1724 was 1724, checked in by Kana Sugimoto, 14 years ago

New Development: Yes

JIRA Issue: Yes (CAS-1801)

Ready to Release: Yes

Interface Changes: Yes

What Interface Changed: two classes, CustomToolbarTkAgg? and CustomToolbarCommon?,

are created to a new module casatoolbar

Test Programs: run sdplot with plottype='spectra' and 'totalpower'

Put in Release Notes: Yes/No?

Module(s): CASA task sdplot, sdbaseline, sdstat, sdfit

Description:

Two new classes, CustomToolbarTkAgg? and CustomToolbarCommon?, are
created to a new module casatoolbar in order to remove backend dependency
of sdplot(). The other code are modified accordingly.
The additional toolbar, casabar, is now default in ASAP plotter
(asapplotter.plot and asapplotter.plottp).


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.3 KB
Line 
1from asap import rcParams, print_log, selector
2from asap import asaplog
3import matplotlib.axes
4import re
5
6class asapplotter:
7    """
8    The ASAP plotter.
9    By default the plotter is set up to plot polarisations
10    'colour stacked' and scantables across panels.
11    Note:
12        Currenly it only plots 'spectra' not Tsys or
13        other variables.
14    """
15    def __init__(self, visible=None):
16        self._visible = rcParams['plotter.gui']
17        if visible is not None:
18            self._visible = visible
19        self._plotter = self._newplotter()
20        if self._visible and matplotlib.get_backend() == "TkAgg":
21            from asap.casatoolbar import CustomToolbarTkAgg
22            self._plotter.figmgr.casabar = CustomToolbarTkAgg(self)
23
24        self._panelling = None
25        self._stacking = None
26        self.set_panelling()
27        self.set_stacking()
28        self._rows = None
29        self._cols = None
30        self._autoplot = False
31        self._minmaxx = None
32        self._minmaxy = None
33        self._datamask = None
34        self._data = None
35        self._lmap = None
36        self._title = None
37        self._ordinate = None
38        self._abcissa = None
39        self._abcunit = None
40        self._usermask = []
41        self._maskselection = None
42        self._selection = selector()
43        self._hist = rcParams['plotter.histogram']
44        self._panellayout = self.set_panellayout(refresh=False)
45
46    def _translate(self, instr):
47        keys = "s b i p t".split()
48        if isinstance(instr, str):
49            for key in keys:
50                if instr.lower().startswith(key):
51                    return key
52        return None
53
54    def _newplotter(self):
55        backend=matplotlib.get_backend()
56        if not self._visible:
57            from asap.asaplot import asaplot
58        elif backend == 'TkAgg':
59            from asap.asaplotgui import asaplotgui as asaplot
60        elif backend == 'Qt4Agg':
61            from asap.asaplotgui_qt4 import asaplotgui as asaplot
62        elif backend == 'GTkAgg':
63            from asap.asaplotgui_gtk import asaplotgui as asaplot
64        else:
65            from asap.asaplot import asaplot
66        return asaplot()
67
68
69    def plot(self, scan=None):
70        """
71        Plot a scantable.
72        Parameters:
73            scan:   a scantable
74        Note:
75            If a scantable was specified in a previous call
76            to plot, no argument has to be given to 'replot'
77            NO checking is done that the abcissas of the scantable
78            are consistent e.g. all 'channel' or all 'velocity' etc.
79        """
80        if self._plotter.is_dead:
81            self._plotter = self._newplotter()
82        self._plotter.hold()
83        self._plotter.clear()
84        if not self._data and not scan:
85            msg = "Input is not a scantable"
86            if rcParams['verbose']:
87                #print msg
88                asaplog.push( msg )
89                print_log( 'ERROR' )
90                return
91            raise TypeError(msg)
92        if scan: self.set_data(scan,refresh=False)
93        self._plot(self._data)
94        if self._minmaxy is not None:
95            self._plotter.set_limits(ylim=self._minmaxy)
96        if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.enable_button()
97        self._plotter.release()
98        self._plotter.tidy()
99        self._plotter.show(hardrefresh=False)
100        print_log()
101        return
102
103
104    # forwards to matplotlib axes
105    def text(self, *args, **kwargs):
106        self._axes_callback("text", *args, **kwargs)
107    text.__doc__ = matplotlib.axes.Axes.text.__doc__
108    def arrow(self, *args, **kwargs):
109        self._axes_callback("arrow", *args, **kwargs)
110    arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
111    def axvline(self, *args, **kwargs):
112        self._axes_callback("axvline", *args, **kwargs)
113    axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
114    def axhline(self, *args, **kwargs):
115        self._axes_callback("axhline", *args, **kwargs)
116    axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
117    def axvspan(self, *args, **kwargs):
118        self._axes_callback("axvspan", *args, **kwargs)
119        # hack to preventy mpl from redrawing the patch
120        # it seem to convert the patch into lines on every draw.
121        # This doesn't happen in a test script???
122        del self._plotter.axes.patches[-1]
123    axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
124
125    def axhspan(self, *args, **kwargs):
126        self._axes_callback("axhspan", *args, **kwargs)
127        # hack to preventy mpl from redrawing the patch
128        # it seem to convert the patch into lines on every draw.
129        # This doesn't happen in a test script???
130        del self._plotter.axes.patches[-1]
131    axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
132
133    def _axes_callback(self, axesfunc, *args, **kwargs):
134        panel = 0
135        if kwargs.has_key("panel"):
136            panel = kwargs.pop("panel")
137        coords = None
138        if kwargs.has_key("coords"):
139            coords = kwargs.pop("coords")
140            if coords.lower() == 'world':
141                kwargs["transform"] = self._plotter.axes.transData
142            elif coords.lower() == 'relative':
143                kwargs["transform"] = self._plotter.axes.transAxes
144        self._plotter.subplot(panel)
145        self._plotter.axes.set_autoscale_on(False)
146        getattr(self._plotter.axes, axesfunc)(*args, **kwargs)
147        self._plotter.show(False)
148        self._plotter.axes.set_autoscale_on(True)
149    # end matplotlib.axes fowarding functions
150
151    def set_data(self, scan, refresh=True):
152        """
153        Set a scantable to plot.
154        Parameters:
155            scan:      a scantable
156            refresh:   True (default) or False. If True, the plot is
157                       replotted based on the new parameter setting(s).
158                       Otherwise,the parameter(s) are set without replotting.
159        Note:
160           The user specified masks and data selections will be reset
161           if a new scantable is set. This method should be called before
162           setting data selections (set_selection) and/or masks (set_mask).
163        """
164        from asap import scantable
165        if isinstance(scan, scantable):
166            if self._data is not None:
167                if scan != self._data:
168                    self._data = scan
169                    # reset
170                    self._reset()
171                    msg = "A new scantable is set to the plotter. The masks and data selections are reset."
172                    asaplog.push( msg )
173                    print_log( 'INFO' )
174            else:
175                self._data = scan
176                self._reset()
177        else:
178            msg = "Input is not a scantable"
179            if rcParams['verbose']:
180                #print msg
181                asaplog.push( msg )
182                print_log( 'ERROR' )
183                return
184            raise TypeError(msg)
185
186        # ranges become invalid when unit changes
187        if self._abcunit and self._abcunit != self._data.get_unit():
188            self._minmaxx = None
189            self._minmaxy = None
190            self._abcunit = self._data.get_unit()
191            self._datamask = None
192        if refresh: self.plot()
193       
194
195    def set_mode(self, stacking=None, panelling=None, refresh=True):
196        """
197        Set the plots look and feel, i.e. what you want to see on the plot.
198        Parameters:
199            stacking:     tell the plotter which variable to plot
200                          as line colour overlays (default 'pol')
201            panelling:    tell the plotter which variable to plot
202                          across multiple panels (default 'scan'
203            refresh:      True (default) or False. If True, the plot is
204                          replotted based on the new parameter setting(s).
205                          Otherwise,the parameter(s) are set without replotting.
206        Note:
207            Valid modes are:
208                 'beam' 'Beam' 'b':     Beams
209                 'if' 'IF' 'i':         IFs
210                 'pol' 'Pol' 'p':       Polarisations
211                 'scan' 'Scan' 's':     Scans
212                 'time' 'Time' 't':     Times
213        """
214        msg = "Invalid mode"
215        if not self.set_panelling(panelling) or \
216               not self.set_stacking(stacking):
217            if rcParams['verbose']:
218                #print msg
219                asaplog.push( msg )
220                print_log( 'ERROR' )
221                return
222            else:
223                raise TypeError(msg)
224        if refresh and self._data: self.plot(self._data)
225        return
226
227    def set_panelling(self, what=None):
228        mode = what
229        if mode is None:
230             mode = rcParams['plotter.panelling']
231        md = self._translate(mode)
232        if md:
233            self._panelling = md
234            self._title = None
235            return True
236        return False
237
238    def set_layout(self,rows=None,cols=None,refresh=True):
239        """
240        Set the multi-panel layout, i.e. how many rows and columns plots
241        are visible.
242        Parameters:
243             rows:   The number of rows of plots
244             cols:   The number of columns of plots
245             refresh:  True (default) or False. If True, the plot is
246                       replotted based on the new parameter setting(s).
247                       Otherwise,the parameter(s) are set without replotting.
248        Note:
249             If no argument is given, the potter reverts to its auto-plot
250             behaviour.
251        """
252        self._rows = rows
253        self._cols = cols
254        if refresh and self._data: self.plot(self._data)
255        return
256
257    def set_stacking(self, what=None):
258        mode = what
259        if mode is None:
260             mode = rcParams['plotter.stacking']
261        md = self._translate(mode)
262        if md:
263            self._stacking = md
264            self._lmap = None
265            return True
266        return False
267
268    def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True):
269        """
270        Set the range of interest on the abcissa of the plot
271        Parameters:
272            [x,y]start,[x,y]end:  The start and end points of the 'zoom' window
273            refresh:  True (default) or False. If True, the plot is
274                      replotted based on the new parameter setting(s).
275                      Otherwise,the parameter(s) are set without replotting.
276        Note:
277            These become non-sensical when the unit changes.
278            use plotter.set_range() without parameters to reset
279
280        """
281        if xstart is None and xend is None:
282            self._minmaxx = None
283        else:
284            self._minmaxx = [xstart,xend]
285        if ystart is None and yend is None:
286            self._minmaxy = None
287        else:
288            self._minmaxy = [ystart,yend]
289        if refresh and self._data: self.plot(self._data)
290        return
291
292    def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
293        """
294        Specify a mapping for the legend instead of using the default
295        indices:
296        Parameters:
297            mp:        a list of 'strings'. This should have the same length
298                       as the number of elements on the legend and then maps
299                       to the indeces in order. It is possible to uses latex
300                       math expression. These have to be enclosed in r'',
301                       e.g. r'$x^{2}$'
302            fontsize:  The font size of the label (default None)
303            mode:      where to display the legend
304                       Any other value for loc else disables the legend:
305                        0: auto
306                        1: upper right
307                        2: upper left
308                        3: lower left
309                        4: lower right
310                        5: right
311                        6: center left
312                        7: center right
313                        8: lower center
314                        9: upper center
315                        10: center
316            refresh:    True (default) or False. If True, the plot is
317                        replotted based on the new parameter setting(s).
318                        Otherwise,the parameter(s) are set without replotting.
319
320        Example:
321             If the data has two IFs/rest frequencies with index 0 and 1
322             for CO and SiO:
323             plotter.set_stacking('i')
324             plotter.set_legend(['CO','SiO'])
325             plotter.plot()
326             plotter.set_legend([r'$^{12}CO$', r'SiO'])
327        """
328        self._lmap = mp
329        self._plotter.legend(mode)
330        if isinstance(fontsize, int):
331            from matplotlib import rc as rcp
332            rcp('legend', fontsize=fontsize)
333        if refresh and self._data: self.plot(self._data)
334        return
335
336    def set_title(self, title=None, fontsize=None, refresh=True):
337        """
338        Set the title of the plot. If multiple panels are plotted,
339        multiple titles have to be specified.
340        Parameters:
341            refresh:    True (default) or False. If True, the plot is
342                        replotted based on the new parameter setting(s).
343                        Otherwise,the parameter(s) are set without replotting.
344        Example:
345             # two panels are visible on the plotter
346             plotter.set_title(["First Panel","Second Panel"])
347        """
348        self._title = title
349        if isinstance(fontsize, int):
350            from matplotlib import rc as rcp
351            rcp('axes', titlesize=fontsize)
352        if refresh and self._data: self.plot(self._data)
353        return
354
355    def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
356        """
357        Set the y-axis label of the plot. If multiple panels are plotted,
358        multiple labels have to be specified.
359        Parameters:
360            ordinate:    a list of ordinate labels. None (default) let
361                         data determine the labels
362            refresh:     True (default) or False. If True, the plot is
363                         replotted based on the new parameter setting(s).
364                         Otherwise,the parameter(s) are set without replotting.
365        Example:
366             # two panels are visible on the plotter
367             plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
368        """
369        self._ordinate = ordinate
370        if isinstance(fontsize, int):
371            from matplotlib import rc as rcp
372            rcp('axes', labelsize=fontsize)
373            rcp('ytick', labelsize=fontsize)
374        if refresh and self._data: self.plot(self._data)
375        return
376
377    def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
378        """
379        Set the x-axis label of the plot. If multiple panels are plotted,
380        multiple labels have to be specified.
381        Parameters:
382            abcissa:     a list of abcissa labels. None (default) let
383                         data determine the labels
384            refresh:     True (default) or False. If True, the plot is
385                         replotted based on the new parameter setting(s).
386                         Otherwise,the parameter(s) are set without replotting.
387        Example:
388             # two panels are visible on the plotter
389             plotter.set_ordinate(["First X-Axis","Second X-Axis"])
390        """
391        self._abcissa = abcissa
392        if isinstance(fontsize, int):
393            from matplotlib import rc as rcp
394            rcp('axes', labelsize=fontsize)
395            rcp('xtick', labelsize=fontsize)
396        if refresh and self._data: self.plot(self._data)
397        return
398
399    def set_colors(self, colmap, refresh=True):
400        """
401        Set the colours to be used. The plotter will cycle through
402        these colours when lines are overlaid (stacking mode).
403        Parameters:
404            colmap:     a list of colour names
405            refresh:    True (default) or False. If True, the plot is
406                        replotted based on the new parameter setting(s).
407                        Otherwise,the parameter(s) are set without replotting.
408        Example:
409             plotter.set_colors("red green blue")
410             # If for example four lines are overlaid e.g I Q U V
411             # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
412             # and 'V' will be 'red' again.
413        """
414        if isinstance(colmap,str):
415            colmap = colmap.split()
416        self._plotter.palette(0, colormap=colmap)
417        if refresh and self._data: self.plot(self._data)
418
419    # alias for english speakers
420    set_colours = set_colors
421
422    def set_histogram(self, hist=True, linewidth=None, refresh=True):
423        """
424        Enable/Disable histogram-like plotting.
425        Parameters:
426            hist:        True (default) or False. The fisrt default
427                         is taken from the .asaprc setting
428                         plotter.histogram
429            refresh:     True (default) or False. If True, the plot is
430                         replotted based on the new parameter setting(s).
431                         Otherwise,the parameter(s) are set without replotting.
432        """
433        self._hist = hist
434        if isinstance(linewidth, float) or isinstance(linewidth, int):
435            from matplotlib import rc as rcp
436            rcp('lines', linewidth=linewidth)
437        if refresh and self._data: self.plot(self._data)
438
439    def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
440        """
441        Set the linestyles to be used. The plotter will cycle through
442        these linestyles when lines are overlaid (stacking mode) AND
443        only one color has been set.
444        Parameters:
445             linestyles:     a list of linestyles to use.
446                             'line', 'dashed', 'dotted', 'dashdot',
447                             'dashdotdot' and 'dashdashdot' are
448                             possible
449            refresh:         True (default) or False. If True, the plot is
450                             replotted based on the new parameter setting(s).
451                             Otherwise,the parameter(s) are set without replotting.
452        Example:
453             plotter.set_colors("black")
454             plotter.set_linestyles("line dashed dotted dashdot")
455             # If for example four lines are overlaid e.g I Q U V
456             # 'I' will be 'solid', 'Q' will be 'dashed',
457             # U will be 'dotted' and 'V' will be 'dashdot'.
458        """
459        if isinstance(linestyles,str):
460            linestyles = linestyles.split()
461        self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
462        if isinstance(linewidth, float) or isinstance(linewidth, int):
463            from matplotlib import rc as rcp
464            rcp('lines', linewidth=linewidth)
465        if refresh and self._data: self.plot(self._data)
466
467    def set_font(self, family=None, style=None, weight=None, size=None, refresh=True):
468        """
469        Set font properties.
470        Parameters:
471            family:    one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
472            style:     one of 'normal' (or 'roman'), 'italic'  or 'oblique'
473            weight:    one of 'normal or 'bold'
474            size:      the 'general' font size, individual elements can be adjusted
475                       seperately
476            refresh:   True (default) or False. If True, the plot is
477                       replotted based on the new parameter setting(s).
478                       Otherwise,the parameter(s) are set without replotting.
479        """
480        from matplotlib import rc as rcp
481        if isinstance(family, str):
482            rcp('font', family=family)
483        if isinstance(style, str):
484            rcp('font', style=style)
485        if isinstance(weight, str):
486            rcp('font', weight=weight)
487        if isinstance(size, float) or isinstance(size, int):
488            rcp('font', size=size)
489        if refresh and self._data: self.plot(self._data)
490
491    def set_panellayout(self,layout=[],refresh=True):
492        """
493        Set the layout of subplots.
494        Parameters:
495            layout:   a list of subplots layout in figure coordinate (0-1),
496                      i.e., fraction of the figure width or height.
497                      The order of elements should be:
498                      [left, bottom, right, top, horizontal space btw panels,
499                      vertical space btw panels].
500            refresh:  True (default) or False. If True, the plot is
501                      replotted based on the new parameter setting(s).
502                      Otherwise,the parameter(s) are set without replotting.
503        Note
504        * When layout is not specified, the values are reset to the defaults
505          of matplotlib.
506        * If any element is set to be None, the current value is adopted.
507        """
508        if layout == []: self._panellayout=self._reset_panellayout()
509        else:
510            self._panellayout=[None]*6
511            self._panellayout[0:len(layout)]=layout
512        #print "panel layout set to ",self._panellayout
513        if refresh and self._data: self.plot(self._data)
514
515    def _reset_panellayout(self):
516        ks=map(lambda x: 'figure.subplot.'+x,
517               ['left','bottom','right','top','hspace','wspace'])
518        return map(matplotlib.rcParams.get,ks)
519
520    def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
521                   location=None):
522        """
523        Plot a line catalog.
524        Parameters:
525            linecat:      the linecatalog to plot
526            doppler:      the velocity shift to apply to the frequencies
527            deltachan:    the number of channels to include each side of the
528                          line to determine a local maximum/minimum
529            rotate:       the rotation (in degrees) )for the text label (default 90.0)
530            location:     the location of the line annotation from the 'top',
531                          'bottom' or alternate (None - the default)
532        Notes:
533        If the spectrum is flagged no line will be drawn in that location.
534        """
535        if not self._data:
536            raise RuntimeError("No scantable has been plotted yet.")
537        from asap._asap import linecatalog
538        if not isinstance(linecat, linecatalog):
539            raise ValueError("'linecat' isn't of type linecatalog.")
540        if not self._data.get_unit().endswith("Hz"):
541            raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
542        from matplotlib.numerix import ma
543        for j in range(len(self._plotter.subplots)):
544            self._plotter.subplot(j)
545            lims = self._plotter.axes.get_xlim()
546            for row in range(linecat.nrow()):
547                # get_frequency returns MHz
548                base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
549                restf = linecat.get_frequency(row)/base[self._data.get_unit()]
550                c = 299792.458
551                freq = restf*(1.0-doppler/c)
552                if lims[0] < freq < lims[1]:
553                    if location is None:
554                        loc = 'bottom'
555                        if row%2: loc='top'
556                    else: loc = location
557                    maxys = []
558                    for line in self._plotter.axes.lines:
559                        v = line._x
560                        asc = v[0] < v[-1]
561
562                        idx = None
563                        if not asc:
564                            if v[len(v)-1] <= freq <= v[0]:
565                                i = len(v)-1
566                                while i>=0 and v[i] < freq:
567                                    idx = i
568                                    i-=1
569                        else:
570                           if v[0] <= freq <= v[len(v)-1]:
571                                i = 0
572                                while  i<len(v) and v[i] < freq:
573                                    idx = i
574                                    i+=1
575                        if idx is not None:
576                            lower = idx - deltachan
577                            upper = idx + deltachan
578                            if lower < 0: lower = 0
579                            if upper > len(v): upper = len(v)
580                            s = slice(lower, upper)
581                            y = line._y[s]
582                            maxy = ma.maximum(y)
583                            if isinstance( maxy, float):
584                                maxys.append(maxy)
585                    if len(maxys):
586                        peak = max(maxys)
587                        if peak > self._plotter.axes.get_ylim()[1]:
588                            loc = 'bottom'
589                    else:
590                        continue
591                    self._plotter.vline_with_label(freq, peak,
592                                                   linecat.get_name(row),
593                                                   location=loc, rotate=rotate)
594        self._plotter.show(hardrefresh=False)
595
596
597    def save(self, filename=None, orientation=None, dpi=None):
598        """
599        Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
600        Parameters:
601             filename:    The name of the output file. This is optional
602                          and autodetects the image format from the file
603                          suffix. If non filename is specified a file
604                          called 'yyyymmdd_hhmmss.png' is created in the
605                          current directory.
606             orientation: optional parameter for postscript only (not eps).
607                          'landscape', 'portrait' or None (default) are valid.
608                          If None is choosen for 'ps' output, the plot is
609                          automatically oriented to fill the page.
610             dpi:         The dpi of the output non-ps plot
611        """
612        self._plotter.save(filename,orientation,dpi)
613        return
614
615
616    def set_mask(self, mask=None, selection=None, refresh=True):
617        """
618        Set a plotting mask for a specific polarization.
619        This is useful for masking out "noise" Pangle outside a source.
620        Parameters:
621             mask:           a mask from scantable.create_mask
622             selection:      the spectra to apply the mask to.
623             refresh:        True (default) or False. If True, the plot is
624                             replotted based on the new parameter setting(s).
625                             Otherwise,the parameter(s) are set without replotting.
626        Example:
627             select = selector()
628             select.setpolstrings("Pangle")
629             plotter.set_mask(mymask, select)
630        """
631        if not self._data:
632            msg = "Can only set mask after a first call to plot()"
633            if rcParams['verbose']:
634                #print msg
635                asaplog.push( msg )
636                print_log( 'ERROR' )
637                return
638            else:
639                raise RuntimeError(msg)
640        if len(mask):
641            if isinstance(mask, list) or isinstance(mask, tuple):
642                self._usermask = array(mask)
643            else:
644                self._usermask = mask
645        if mask is None and selection is None:
646            self._usermask = []
647            self._maskselection = None
648        if isinstance(selection, selector):
649            self._maskselection = {'b': selection.get_beams(),
650                                   's': selection.get_scans(),
651                                   'i': selection.get_ifs(),
652                                   'p': selection.get_pols(),
653                                   't': [] }
654        else:
655            self._maskselection = None
656        if refresh: self.plot(self._data)
657
658    def _slice_indeces(self, data):
659        mn = self._minmaxx[0]
660        mx = self._minmaxx[1]
661        asc = data[0] < data[-1]
662        start=0
663        end = len(data)-1
664        inc = 1
665        if not asc:
666            start = len(data)-1
667            end = 0
668            inc = -1
669        # find min index
670        #while start > 0 and data[start] < mn:
671        #    start+= inc
672        minind=start
673        for ind in xrange(start,end+inc,inc):
674            if data[ind] > mn: break
675            minind=ind
676        # find max index
677        #while end > 0 and data[end] > mx:
678        #    end-=inc
679        #if end > 0: end +=1
680        maxind=end
681        for ind in xrange(end,start-inc,-inc):
682            if data[ind] < mx: break
683            maxind=ind
684        start=minind
685        end=maxind
686        if start > end:
687            return end,start+1
688        elif start < end:
689            return start,end+1
690        else:
691            return start,end
692
693    def _reset(self):
694        self._usermask = []
695        self._usermaskspectra = None
696        self.set_selection(None, False)
697
698    def _plot(self, scan):
699        savesel = scan.get_selection()
700        sel = savesel +  self._selection
701        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
702              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
703        order = [d0[self._panelling],d0[self._stacking]]
704        sel.set_order(order)
705        scan.set_selection(sel)
706        d = {'b': scan.getbeam, 's': scan.getscan,
707             'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
708
709        polmodes = dict(zip(self._selection.get_pols(),
710                            self._selection.get_poltypes()))
711        # this returns either a tuple of numbers or a length  (ncycles)
712        # convert this into lengths
713        n0,nstack0 = self._get_selected_n(scan)
714        if isinstance(n0, int): n = n0
715        else: n = len(n0)
716        if isinstance(nstack0, int): nstack = nstack0
717        else: nstack = len(nstack0)
718        maxpanel, maxstack = 16,8
719        if n > maxpanel or nstack > maxstack:
720            maxn = 0
721            if nstack > maxstack: maxn = maxstack
722            if n > maxpanel: maxn = maxpanel
723            msg ="Scan to be plotted contains more than %d selections.\n" \
724                  "Selecting first %d selections..." % (maxn, maxn)
725            asaplog.push(msg)
726            print_log('WARN')
727            n = min(n,maxpanel)
728            nstack = min(nstack,maxstack)
729        if n > 1:
730            ganged = rcParams['plotter.ganged']
731            if self._panelling == 'i':
732                ganged = False
733            if self._rows and self._cols:
734                n = min(n,self._rows*self._cols)
735                self._plotter.set_panels(rows=self._rows,cols=self._cols,
736#                                         nplots=n,ganged=ganged)
737                                         nplots=n,layout=self._panellayout,ganged=ganged)
738            else:
739#                self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
740                self._plotter.set_panels(rows=n,cols=0,nplots=n,layout=self._panellayout,ganged=ganged)
741        else:
742#            self._plotter.set_panels()
743            self._plotter.set_panels(layout=self._panellayout)
744        r=0
745        nr = scan.nrow()
746        a0,b0 = -1,-1
747        allxlim = []
748        allylim = []
749        newpanel=True
750        panelcount,stackcount = 0,0
751        while r < nr:
752            a = d[self._panelling](r)
753            b = d[self._stacking](r)
754            if a > a0 and panelcount < n:
755                if n > 1:
756                    self._plotter.subplot(panelcount)
757                self._plotter.palette(0)
758                #title
759                xlab = self._abcissa and self._abcissa[panelcount] \
760                       or scan._getabcissalabel()
761                ylab = self._ordinate and self._ordinate[panelcount] \
762                       or scan._get_ordinate_label()
763                self._plotter.set_axes('xlabel',xlab)
764                self._plotter.set_axes('ylabel',ylab)
765                lbl = self._get_label(scan, r, self._panelling, self._title)
766                if isinstance(lbl, list) or isinstance(lbl, tuple):
767                    if 0 <= panelcount < len(lbl):
768                        lbl = lbl[panelcount]
769                    else:
770                        # get default label
771                        lbl = self._get_label(scan, r, self._panelling, None)
772                self._plotter.set_axes('title',lbl)
773                newpanel = True
774                stackcount =0
775                panelcount += 1
776            if (b > b0 or newpanel) and stackcount < nstack:
777                y = []
778                if len(polmodes):
779                    y = scan._getspectrum(r, polmodes[scan.getpol(r)])
780                else:
781                    y = scan._getspectrum(r)
782                m = scan._getmask(r)
783                from matplotlib.numerix import logical_not, logical_and
784                if self._maskselection and len(self._usermask) == len(m):
785                    if d[self._stacking](r) in self._maskselection[self._stacking]:
786                        m = logical_and(m, self._usermask)
787                x = scan._getabcissa(r)
788                from matplotlib.numerix import ma, array
789                y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
790                if self._minmaxx is not None:
791                    s,e = self._slice_indeces(x)
792                    x = x[s:e]
793                    y = y[s:e]
794                if len(x) > 1024 and rcParams['plotter.decimate']:
795                    fac = len(x)/1024
796                    x = x[::fac]
797                    y = y[::fac]
798                llbl = self._get_label(scan, r, self._stacking, self._lmap)
799                if isinstance(llbl, list) or isinstance(llbl, tuple):
800                    if 0 <= stackcount < len(llbl):
801                        # use user label
802                        llbl = llbl[stackcount]
803                    else:
804                        # get default label
805                        llbl = self._get_label(scan, r, self._stacking, None)
806                self._plotter.set_line(label=llbl)
807                plotit = self._plotter.plot
808                if self._hist: plotit = self._plotter.hist
809                if len(x) > 0:
810                    plotit(x,y)
811                    xlim= self._minmaxx or [min(x),max(x)]
812                    allxlim += xlim
813                    ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
814                    allylim += ylim
815                else:
816                    xlim = self._minmaxx or []
817                    allxlim += xlim
818                    ylim= self._minmaxy or []
819                    allylim += ylim
820                stackcount += 1
821                # last in colour stack -> autoscale x
822                if stackcount == nstack and len(allxlim) > 0:
823                    allxlim.sort()
824                    self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
825                    # clear
826                    allxlim =[]
827
828            newpanel = False
829            a0=a
830            b0=b
831            # ignore following rows
832            if (panelcount == n) and (stackcount == nstack):
833                # last panel -> autoscale y if ganged
834                if rcParams['plotter.ganged'] and len(allylim) > 0:
835                    allylim.sort()
836                    self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
837                break
838            r+=1 # next row
839        #reset the selector to the scantable's original
840        scan.set_selection(savesel)
841
842    def set_selection(self, selection=None, refresh=True):
843        """
844        Parameters:
845            selection:  a selector object (default unset the selection)
846            refresh:    True (default) or False. If True, the plot is
847                        replotted based on the new parameter setting(s).
848                        Otherwise,the parameter(s) are set without replotting.
849        """
850        self._selection = isinstance(selection,selector) and selection or selector()
851        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
852              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
853        order = [d0[self._panelling],d0[self._stacking]]
854        self._selection.set_order(order)
855        if refresh and self._data: self.plot(self._data)
856
857    def _get_selected_n(self, scan):
858        d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
859             'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle }
860        d2 = { 'b': self._selection.get_beams(),
861               's': self._selection.get_scans(),
862               'i': self._selection.get_ifs(),
863               'p': self._selection.get_pols(),
864               't': self._selection.get_cycles() }
865        n =  d2[self._panelling] or d1[self._panelling]()
866        nstack = d2[self._stacking] or d1[self._stacking]()
867        return n,nstack
868
869    def _get_label(self, scan, row, mode, userlabel=None):
870        if isinstance(userlabel, list) and len(userlabel) == 0:
871            userlabel = " "
872        pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
873        if len(pms):
874            poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
875        else:
876            poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
877        d = {'b': "Beam "+str(scan.getbeam(row)),
878             #'s': scan._getsourcename(row),
879             's': "Scan "+str(scan.getscan(row))+\
880                  " ("+str(scan._getsourcename(row))+")",
881             'i': "IF"+str(scan.getif(row)),
882             'p': poleval,
883             't': str(scan.get_time(row)) }
884        return userlabel or d[mode]
885
886    def plotazel(self, scan=None, outfile=None):
887        """
888        plot azimuth and elevation  versus time of a scantable
889        """
890        import pylab as PL
891        from matplotlib.dates import DateFormatter, timezone, HourLocator, MinuteLocator, DayLocator
892        from matplotlib.ticker import MultipleLocator
893        from matplotlib.numerix import array, pi
894        self._data = scan
895        self._outfile = outfile
896        dates = self._data.get_time(asdatetime=True)
897        t = PL.date2num(dates)
898        tz = timezone('UTC')
899        PL.cla()
900        #PL.ioff()
901        PL.clf()
902        # Adjust subplot layouts
903        if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
904        lef, bot, rig, top, wsp, hsp = self._panellayout
905        PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
906                                 wspace=wsp,hspace=hsp)
907       
908        tdel = max(t) - min(t)
909        ax = PL.subplot(2,1,1)
910        el = array(self._data.get_elevation())*180./pi
911        PL.ylabel('El [deg.]')
912        dstr = dates[0].strftime('%Y/%m/%d')
913        if tdel > 1.0:
914            dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
915            dstr = dstr + " - " + dstr2
916            majloc = DayLocator()
917            minloc = HourLocator(range(0,23,12))
918            timefmt = DateFormatter("%b%d")
919        else:
920            timefmt = DateFormatter('%H')
921            majloc = HourLocator()
922            minloc = MinuteLocator(20)
923        PL.title(dstr)
924
925        if tdel == 0.0:
926            th = (t - PL.floor(t))*24.0
927            PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
928        else:
929            PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
930            #ax.grid(True)
931            ax.xaxis.set_major_formatter(timefmt)
932            ax.xaxis.set_major_locator(majloc)
933            ax.xaxis.set_minor_locator(minloc)
934        ax.yaxis.grid(True)
935        yloc = MultipleLocator(30)
936        ax.set_ylim(0,90)
937        ax.yaxis.set_major_locator(yloc)
938        if tdel > 1.0:
939            labels = ax.get_xticklabels()
940        #    PL.setp(labels, fontsize=10, rotation=45)
941            PL.setp(labels, fontsize=10)
942
943        # Az plot
944        az = array(self._data.get_azimuth())*180./pi
945        if min(az) < 0:
946            for irow in range(len(az)):
947                if az[irow] < 0: az[irow] += 360.0
948
949        ax = PL.subplot(2,1,2)
950        #PL.xlabel('Time (UT [hour])')
951        PL.ylabel('Az [deg.]')
952        if tdel == 0.0:
953            PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
954        else:
955            PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
956            ax.xaxis.set_major_formatter(timefmt)
957            ax.xaxis.set_major_locator(majloc)
958            ax.xaxis.set_minor_locator(minloc)
959        #ax.grid(True)
960        ax.set_ylim(0,360)
961        ax.yaxis.grid(True)
962        #hfmt = DateFormatter('%H')
963        #hloc = HourLocator()
964        yloc = MultipleLocator(60)
965        ax.yaxis.set_major_locator(yloc)
966        if tdel > 1.0:
967            labels = ax.get_xticklabels()
968            PL.setp(labels, fontsize=10)
969            PL.xlabel('Time (UT [day])')
970        else:
971            PL.xlabel('Time (UT [hour])')
972
973        #PL.ion()
974        PL.draw()
975        if (self._outfile is not None):
976           PL.savefig(self._outfile)
977
978    def plotpointing(self, scan=None, outfile=None):
979        """
980        plot telescope pointings
981        """
982        import pylab as PL
983        from matplotlib.dates import DateFormatter, timezone
984        from matplotlib.ticker import MultipleLocator
985        from matplotlib.numerix import array, pi, zeros
986        self._data = scan
987        self._outfile = outfile
988        dir = array(self._data.get_directionval()).transpose()
989        ra = dir[0]*180./pi
990        dec = dir[1]*180./pi
991        PL.cla()
992        #PL.ioff()
993        PL.clf()
994        # Adjust subplot layouts
995        if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
996        lef, bot, rig, top, wsp, hsp = self._panellayout
997        PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
998                                 wspace=wsp,hspace=hsp)
999        ax = PL.axes([0.1,0.1,0.8,0.8])
1000        ax = PL.axes([0.1,0.1,0.8,0.8])
1001        ax.set_aspect('equal')
1002        PL.plot(ra,dec, 'b,')
1003        PL.xlabel('RA [deg.]')
1004        PL.ylabel('Declination [deg.]')
1005        PL.title('Telescope pointings')
1006        [xmin,xmax,ymin,ymax] = PL.axis()
1007        PL.axis([xmax,xmin,ymin,ymax])
1008        #PL.ion()
1009        PL.draw()
1010        if (self._outfile is not None):
1011           PL.savefig(self._outfile)
1012
1013    # plot total power data
1014    # plotting in time is not yet implemented..
1015    def plottp(self, scan=None, outfile=None):
1016        if self._plotter.is_dead:
1017            self._plotter = self._newplotter()
1018        self._plotter.hold()
1019        self._plotter.clear()
1020        from asap import scantable
1021        if not self._data and not scan:
1022            msg = "Input is not a scantable"
1023            if rcParams['verbose']:
1024                #print msg
1025                asaplog.push( msg )
1026                print_log( 'ERROR' )
1027                return
1028            raise TypeError(msg)
1029        if isinstance(scan, scantable):
1030            if self._data is not None:
1031                if scan != self._data:
1032                    self._data = scan
1033                    # reset
1034                    self._reset()
1035            else:
1036                self._data = scan
1037                self._reset()
1038        # ranges become invalid when abcissa changes?
1039        #if self._abcunit and self._abcunit != self._data.get_unit():
1040        #    self._minmaxx = None
1041        #    self._minmaxy = None
1042        #    self._abcunit = self._data.get_unit()
1043        #    self._datamask = None
1044
1045        # Adjust subplot layouts
1046        if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
1047        lef, bot, rig, top, wsp, hsp = self._panellayout
1048        self._plotter.figure.subplots_adjust(
1049            left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
1050        if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.disable_button()
1051        self._plottp(self._data)
1052        if self._minmaxy is not None:
1053            self._plotter.set_limits(ylim=self._minmaxy)
1054        self._plotter.release()
1055        self._plotter.tidy()
1056        self._plotter.show(hardrefresh=False)
1057        print_log()
1058        return
1059
1060    def _plottp(self,scan):
1061        """
1062        private method for plotting total power data
1063        """
1064        from matplotlib.numerix import ma, array, arange, logical_not
1065        r=0
1066        nr = scan.nrow()
1067        a0,b0 = -1,-1
1068        allxlim = []
1069        allylim = []
1070        y=[]
1071        self._plotter.set_panels()
1072        self._plotter.palette(0)
1073        #title
1074        #xlab = self._abcissa and self._abcissa[panelcount] \
1075        #       or scan._getabcissalabel()
1076        #ylab = self._ordinate and self._ordinate[panelcount] \
1077        #       or scan._get_ordinate_label()
1078        xlab = self._abcissa or 'row number' #or Time
1079        ylab = self._ordinate or scan._get_ordinate_label()
1080        self._plotter.set_axes('xlabel',xlab)
1081        self._plotter.set_axes('ylabel',ylab)
1082        lbl = self._get_label(scan, r, 's', self._title)
1083        if isinstance(lbl, list) or isinstance(lbl, tuple):
1084        #    if 0 <= panelcount < len(lbl):
1085        #        lbl = lbl[panelcount]
1086        #    else:
1087                # get default label
1088             lbl = self._get_label(scan, r, self._panelling, None)
1089        self._plotter.set_axes('title',lbl)
1090        y=array(scan._get_column(scan._getspectrum,-1))
1091        m = array(scan._get_column(scan._getmask,-1))
1092        y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1093        x = arange(len(y))
1094        # try to handle spectral data somewhat...
1095        l,m = y.shape
1096        if m > 1:
1097            y=y.mean(axis=1)
1098        plotit = self._plotter.plot
1099        llbl = self._get_label(scan, r, self._stacking, None)
1100        self._plotter.set_line(label=llbl)
1101        if len(x) > 0:
1102            plotit(x,y)
1103
1104
1105    # forwards to matplotlib.Figure.text
1106    def figtext(self, *args, **kwargs):
1107        """
1108        Add text to figure at location x,y (relative 0-1 coords).
1109        This method forwards *args and **kwargs to a Matplotlib method,
1110        matplotlib.Figure.text.
1111        See the method help for detailed information.
1112        """
1113        self._plotter.text(*args, **kwargs)
1114    # end matplotlib.Figure.text forwarding function
1115
1116
1117    # printing header information
1118    def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
1119        """
1120        print data (scantable) header on the plot and/or logger.
1121        Parameters:
1122            plot:      whether or not print header info on the plot.
1123            fontsize:  header font size (valid only plot=True)
1124            autoscale: whether or not autoscale the plot (valid only plot=True)
1125            logger:    whether or not print header info on the logger.
1126            selstr:    additional selection string (not verified)
1127            extrastr:  additional string to print (not verified)
1128        """
1129        if not plot and not logger: return
1130        if not self._data: raise RuntimeError("No scantable has been set yet.")
1131        # Now header will be printed on plot and/or logger.
1132        # Get header information and format it.
1133        ssum=self._data.__str__()
1134        # Print Observation header to the upper-left corner of plot
1135        if plot:
1136            srest=ssum[ssum.find('Rest Freqs:'):ssum.find('Abcissa:')]
1137            shead=ssum[ssum.find('Beams:'):ssum.find('Flux Unit:')]
1138            headstr=shead.split('\n\n')
1139            if extrastr != '': headstr[1]=extrastr+'\n'+headstr[1]
1140            #headstr[1]='Data File:     '+(filestr or 'unknown')+'\n'+headstr[1]
1141            headstr[0]=headstr[0]+'\n'+srest
1142            headstr.reverse()
1143            ssel='***Selections***\n'+(selstr+self._data.get_selection().__str__() or 'none')
1144            headstr.append(ssel)
1145            nstcol=len(headstr)
1146           
1147            self._plotter.hold()
1148            for i in range(nstcol):
1149                self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1150                             headstr[i],
1151                             horizontalalignment='left',
1152                             verticalalignment='top',
1153                             fontsize=fontsize)
1154            import time
1155            self._plotter.figure.text(0.99,0.0,
1156                            time.strftime("%a %d %b %Y  %H:%M:%S %Z"),
1157                            horizontalalignment='right',
1158                            verticalalignment='bottom',fontsize=8)
1159            self._plotter.release()
1160            del srest, shead, headstr, ssel
1161        if logger:
1162            asaplog.push("----------------\n  Plot Summary\n----------------")
1163            asaplog.push(extrastr)
1164            asaplog.push(ssum[ssum.find('Beams:'):])
1165            print_log()
1166        del ssum
1167
1168
Note: See TracBrowser for help on using the repository browser.