source: trunk/python/asapplotter.py @ 1547

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