source: trunk/python/asapplotter.py @ 1559

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

added forward for axes.annotate and added useful default plus interactive mode

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