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

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

New Development: No

JIRA Issue: Yes (CAS-1077)

Ready to Release: Yes

Interface Changes: Yes

What Interface Changed: see the Description

Test Programs: run the new function or try refresh=False

Put in Release Notes: Yes

Module(s): CASA task sdplot()

Description:

  1. A new function, set_data(self, scan, refresh=True), added to the asapplotter class. This function sets a scantable (specified by scan) to be plotted.
  2. A boolean parameter refresh is added to following functions in the asapplotter class:

set_mode, set_layout, set_range, set_legend, set_title, set_ordinate,
set_abcissa, set_colors, set_histogram, set_linestyles, set_font,
set_mask, and set_selection

The plot is replotted based on the new parameter setting(s) only when refresh=True.


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