source: trunk/python/asapplotter.py @ 1548

Last change on this file since 1548 was 1548, checked in by Malte Marquarding, 15 years ago

add interactive annotation capability. Also allow creation of masks interactively

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