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

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

New Development: No

JIRA Issue: Yes (CAS-1817)

Ready to Release: for CASA 3.0.2 Yes

Interface Changes: No

What Interface Changed:

Test Programs: run sdplot with panel='s'

Put in Release Notes: No

Module(s): CASA task, sdplot()

Description:

Show scan number + source name when panel='scan'.
Changed definition of the title font size.


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