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

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

New Development: No

JIRA Issue: Yes (CAS-2064, 1801)

Ready to Release: Yes

Interface Changes: No

What Interface Changed:

Test Programs: run sdplot() with plottype='pointings' and specificaion of layout

Put in Release Notes: No

Module(s):

Description: A bug fix to make layout specification valid for plotpointing


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.4 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.gca()
1000        #ax = PL.axes([0.1,0.1,0.8,0.8])
1001        #ax = PL.axes([0.1,0.1,0.8,0.8])
1002        ax.set_aspect('equal')
1003        PL.plot(ra,dec, 'b,')
1004        PL.xlabel('RA [deg.]')
1005        PL.ylabel('Declination [deg.]')
1006        PL.title('Telescope pointings')
1007        [xmin,xmax,ymin,ymax] = PL.axis()
1008        PL.axis([xmax,xmin,ymin,ymax])
1009        #PL.ion()
1010        PL.draw()
1011        if (self._outfile is not None):
1012           PL.savefig(self._outfile)
1013
1014    # plot total power data
1015    # plotting in time is not yet implemented..
1016    def plottp(self, scan=None, outfile=None):
1017        if self._plotter.is_dead:
1018            self._plotter = self._newplotter()
1019        self._plotter.hold()
1020        self._plotter.clear()
1021        from asap import scantable
1022        if not self._data and not scan:
1023            msg = "Input is not a scantable"
1024            if rcParams['verbose']:
1025                #print msg
1026                asaplog.push( msg )
1027                print_log( 'ERROR' )
1028                return
1029            raise TypeError(msg)
1030        if isinstance(scan, scantable):
1031            if self._data is not None:
1032                if scan != self._data:
1033                    self._data = scan
1034                    # reset
1035                    self._reset()
1036            else:
1037                self._data = scan
1038                self._reset()
1039        # ranges become invalid when abcissa changes?
1040        #if self._abcunit and self._abcunit != self._data.get_unit():
1041        #    self._minmaxx = None
1042        #    self._minmaxy = None
1043        #    self._abcunit = self._data.get_unit()
1044        #    self._datamask = None
1045
1046        # Adjust subplot layouts
1047        if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
1048        lef, bot, rig, top, wsp, hsp = self._panellayout
1049        self._plotter.figure.subplots_adjust(
1050            left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
1051        if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.disable_button()
1052        self._plottp(self._data)
1053        if self._minmaxy is not None:
1054            self._plotter.set_limits(ylim=self._minmaxy)
1055        self._plotter.release()
1056        self._plotter.tidy()
1057        self._plotter.show(hardrefresh=False)
1058        print_log()
1059        return
1060
1061    def _plottp(self,scan):
1062        """
1063        private method for plotting total power data
1064        """
1065        from matplotlib.numerix import ma, array, arange, logical_not
1066        r=0
1067        nr = scan.nrow()
1068        a0,b0 = -1,-1
1069        allxlim = []
1070        allylim = []
1071        y=[]
1072        self._plotter.set_panels()
1073        self._plotter.palette(0)
1074        #title
1075        #xlab = self._abcissa and self._abcissa[panelcount] \
1076        #       or scan._getabcissalabel()
1077        #ylab = self._ordinate and self._ordinate[panelcount] \
1078        #       or scan._get_ordinate_label()
1079        xlab = self._abcissa or 'row number' #or Time
1080        ylab = self._ordinate or scan._get_ordinate_label()
1081        self._plotter.set_axes('xlabel',xlab)
1082        self._plotter.set_axes('ylabel',ylab)
1083        lbl = self._get_label(scan, r, 's', self._title)
1084        if isinstance(lbl, list) or isinstance(lbl, tuple):
1085        #    if 0 <= panelcount < len(lbl):
1086        #        lbl = lbl[panelcount]
1087        #    else:
1088                # get default label
1089             lbl = self._get_label(scan, r, self._panelling, None)
1090        self._plotter.set_axes('title',lbl)
1091        y=array(scan._get_column(scan._getspectrum,-1))
1092        m = array(scan._get_column(scan._getmask,-1))
1093        y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1094        x = arange(len(y))
1095        # try to handle spectral data somewhat...
1096        l,m = y.shape
1097        if m > 1:
1098            y=y.mean(axis=1)
1099        plotit = self._plotter.plot
1100        llbl = self._get_label(scan, r, self._stacking, None)
1101        self._plotter.set_line(label=llbl)
1102        if len(x) > 0:
1103            plotit(x,y)
1104
1105
1106    # forwards to matplotlib.Figure.text
1107    def figtext(self, *args, **kwargs):
1108        """
1109        Add text to figure at location x,y (relative 0-1 coords).
1110        This method forwards *args and **kwargs to a Matplotlib method,
1111        matplotlib.Figure.text.
1112        See the method help for detailed information.
1113        """
1114        self._plotter.text(*args, **kwargs)
1115    # end matplotlib.Figure.text forwarding function
1116
1117
1118    # printing header information
1119    def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
1120        """
1121        print data (scantable) header on the plot and/or logger.
1122        Parameters:
1123            plot:      whether or not print header info on the plot.
1124            fontsize:  header font size (valid only plot=True)
1125            autoscale: whether or not autoscale the plot (valid only plot=True)
1126            logger:    whether or not print header info on the logger.
1127            selstr:    additional selection string (not verified)
1128            extrastr:  additional string to print (not verified)
1129        """
1130        if not plot and not logger: return
1131        if not self._data: raise RuntimeError("No scantable has been set yet.")
1132        # Now header will be printed on plot and/or logger.
1133        # Get header information and format it.
1134        ssum=self._data.__str__()
1135        # Print Observation header to the upper-left corner of plot
1136        if plot:
1137            srest=ssum[ssum.find('Rest Freqs:'):ssum.find('Abcissa:')]
1138            shead=ssum[ssum.find('Beams:'):ssum.find('Flux Unit:')]
1139            headstr=shead.split('\n\n')
1140            if extrastr != '': headstr[1]=extrastr+'\n'+headstr[1]
1141            #headstr[1]='Data File:     '+(filestr or 'unknown')+'\n'+headstr[1]
1142            headstr[0]=headstr[0]+'\n'+srest
1143            headstr.reverse()
1144            ssel='***Selections***\n'+(selstr+self._data.get_selection().__str__() or 'none')
1145            headstr.append(ssel)
1146            nstcol=len(headstr)
1147           
1148            self._plotter.hold()
1149            for i in range(nstcol):
1150                self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1151                             headstr[i],
1152                             horizontalalignment='left',
1153                             verticalalignment='top',
1154                             fontsize=fontsize)
1155            import time
1156            self._plotter.figure.text(0.99,0.0,
1157                            time.strftime("%a %d %b %Y  %H:%M:%S %Z"),
1158                            horizontalalignment='right',
1159                            verticalalignment='bottom',fontsize=8)
1160            self._plotter.release()
1161            del srest, shead, headstr, ssel
1162        if logger:
1163            asaplog.push("----------------\n  Plot Summary\n----------------")
1164            asaplog.push(extrastr)
1165            asaplog.push(ssum[ssum.find('Beams:'):])
1166            print_log()
1167        del ssum
1168
1169
Note: See TracBrowser for help on using the repository browser.