source: trunk/python/asapplotter.py @ 1739

Last change on this file since 1739 was 1739, checked in by Malte Marquarding, 14 years ago

Replace matplotlib.numerix with numpy

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