source: trunk/python/asapplotter.py @ 1034

Last change on this file since 1034 was 1034, checked in by mar637, 18 years ago

Removed debug print

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.7 KB
Line 
1from asap import rcParams, print_log, selector
2from numarray import logical_and
3
4class asapplotter:
5    """
6    The ASAP plotter.
7    By default the plotter is set up to plot polarisations
8    'colour stacked' and scantables across panels.
9    Note:
10        Currenly it only plots 'spectra' not Tsys or
11        other variables.
12    """
13    def __init__(self, visible=None):
14        self._visible = rcParams['plotter.gui']
15        if visible is not None:
16            self._visible = visible
17        self._plotter = self._newplotter()
18
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            print "please provide a scantable to plot"
75        if isinstance(scan, scantable):
76            if self._data is not None:
77                if scan != self._data:
78                    self._data = scan
79                    # reset
80                    self._reset()
81            else:
82                self._data = scan
83                self._reset()
84        # ranges become invalid when unit changes
85        if self._abcunit and self._abcunit != self._data.get_unit():
86            self._minmaxx = None
87            self._minmaxy = None
88            self._abcunit = self._data.get_unit()
89            self._datamask = None
90        self._plot(self._data)
91        if self._minmaxy is not None:
92            self._plotter.set_limits(ylim=self._minmaxy)
93        self._plotter.release()
94        print_log()
95        return
96
97    def set_mode(self, stacking=None, panelling=None):
98        """
99        Set the plots look and feel, i.e. what you want to see on the plot.
100        Parameters:
101            stacking:     tell the plotter which variable to plot
102                          as line color overlays (default 'pol')
103            panelling:    tell the plotter which variable to plot
104                          across multiple panels (default 'scan'
105        Note:
106            Valid modes are:
107                 'beam' 'Beam' 'b':     Beams
108                 'if' 'IF' 'i':         IFs
109                 'pol' 'Pol' 'p':       Polarisations
110                 'scan' 'Scan' 's':     Scans
111                 'time' 'Time' 't':     Times
112        """
113        msg = "Invalid mode"
114        if not self.set_panelling(panelling) or \
115               not self.set_stacking(stacking):
116            if rcParams['verbose']:
117                print msg
118                return
119            else:
120                raise TypeError(msg)
121        if self._data: self.plot(self._data)
122        return
123
124    def set_panelling(self, what=None):
125        mode = what
126        if mode is None:
127             mode = rcParams['plotter.panelling']
128        md = self._translate(mode)
129        if md:
130            self._panelling = md
131            self._title = None
132            return True
133        return False
134
135    def set_layout(self,rows=None,cols=None):
136        """
137        Set the multi-panel layout, i.e. how many rows and columns plots
138        are visible.
139        Parameters:
140             rows:   The number of rows of plots
141             cols:   The number of columns of plots
142        Note:
143             If no argument is given, the potter reverts to its auto-plot
144             behaviour.
145        """
146        self._rows = rows
147        self._cols = cols
148        if self._data: self.plot(self._data)
149        return
150
151    def set_stacking(self, what=None):
152        mode = what
153        if mode is None:
154             mode = rcParams['plotter.stacking']
155        md = self._translate(mode)
156        if md:
157            self._stacking = md
158            self._lmap = None
159            return True
160        return False
161
162    def set_range(self,xstart=None,xend=None,ystart=None,yend=None):
163        """
164        Set the range of interest on the abcissa of the plot
165        Parameters:
166            [x,y]start,[x,y]end:  The start and end points of the 'zoom' window
167        Note:
168            These become non-sensical when the unit changes.
169            use plotter.set_range() without parameters to reset
170
171        """
172        if xstart is None and xend is None:
173            self._minmaxx = None
174        else:
175            self._minmaxx = [xstart,xend]
176        if ystart is None and yend is None:
177            self._minmaxy = None
178        else:
179            self._minmaxy = [ystart,yend]
180        if self._data: self.plot(self._data)
181        return
182
183    def set_legend(self, mp=None):
184        """
185        Specify a mapping for the legend instead of using the default
186        indices:
187        Parameters:
188             mp:    a list of 'strings'. This should have the same length
189                    as the number of elements on the legend and then maps
190                    to the indeces in order. It is possible to uses latex
191                    math expression. These have to be enclosed in r'', e.g. r'$x^{2}$'
192
193        Example:
194             If the data has two IFs/rest frequencies with index 0 and 1
195             for CO and SiO:
196             plotter.set_stacking('i')
197             plotter.set_legend(['CO','SiO'])
198             plotter.plot()
199             plotter.set_legend([r'$^{12}CO$', r'SiO'])
200        """
201        self._lmap = mp
202        if self._data: self.plot(self._data)
203        return
204
205    def set_title(self, title=None):
206        """
207        Set the title of the plot. If multiple panels are plotted,
208        multiple titles have to be specified.
209        Example:
210             # two panels are visible on the plotter
211             plotter.set_title(["First Panel","Second Panel"])
212        """
213        self._title = title
214        if self._data: self.plot(self._data)
215        return
216
217    def set_ordinate(self, ordinate=None):
218        """
219        Set the y-axis label of the plot. If multiple panels are plotted,
220        multiple labels have to be specified.
221        Parameters:
222            ordinate:    a list of ordinate labels. None (default) let
223                         data determine the labels
224        Example:
225             # two panels are visible on the plotter
226             plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
227        """
228        self._ordinate = ordinate
229        if self._data: self.plot(self._data)
230        return
231
232    def set_abcissa(self, abcissa=None):
233        """
234        Set the x-axis label of the plot. If multiple panels are plotted,
235        multiple labels have to be specified.
236        Parameters:
237            abcissa:     a list of abcissa labels. None (default) let
238                         data determine the labels
239        Example:
240             # two panels are visible on the plotter
241             plotter.set_ordinate(["First X-Axis","Second X-Axis"])
242        """
243        self._abcissa = abcissa
244        if self._data: self.plot(self._data)
245        return
246
247    def set_colors(self, colormap):
248        """
249        Set the colors to be used. The plotter will cycle through
250        these colors when lines are overlaid (stacking mode).
251        Parameters:
252            colormap:     a list of colour names
253        Example:
254             plotter.set_colors("red green blue")
255             # If for example four lines are overlaid e.g I Q U V
256             # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
257             # and 'V' will be 'red' again.
258        """
259        if isinstance(colormap,str):
260            colormap = colormap.split()
261        self._plotter.palette(0,colormap=colormap)
262        if self._data: self.plot(self._data)
263
264    def set_histogram(self, hist=True):
265        """
266        Enable/Disable histogram-like plotting.
267        Parameters:
268            hist:        True (default) or False. The fisrt default
269                         is taken from the .asaprc setting
270                         plotter.histogram
271        """
272        self._hist = hist
273        if self._data: self.plot(self._data)
274
275    def set_linestyles(self, linestyles):
276        """
277        Set the linestyles to be used. The plotter will cycle through
278        these linestyles when lines are overlaid (stacking mode) AND
279        only one color has been set.
280        Parameters:
281             linestyles:     a list of linestyles to use.
282                             'line', 'dashed', 'dotted', 'dashdot',
283                             'dashdotdot' and 'dashdashdot' are
284                             possible
285
286        Example:
287             plotter.set_colors("black")
288             plotter.set_linestyles("line dashed dotted dashdot")
289             # If for example four lines are overlaid e.g I Q U V
290             # 'I' will be 'solid', 'Q' will be 'dashed',
291             # U will be 'dotted' and 'V' will be 'dashdot'.
292        """
293        if isinstance(linestyles,str):
294            linestyles = linestyles.split()
295        self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
296        if self._data: self.plot(self._data)
297
298    def save(self, filename=None, orientation=None, dpi=None):
299        """
300        Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
301        Parameters:
302             filename:    The name of the output file. This is optional
303                          and autodetects the image format from the file
304                          suffix. If non filename is specified a file
305                          called 'yyyymmdd_hhmmss.png' is created in the
306                          current directory.
307             orientation: optional parameter for postscript only (not eps).
308                          'landscape', 'portrait' or None (default) are valid.
309                          If None is choosen for 'ps' output, the plot is
310                          automatically oriented to fill the page.
311             dpi:         The dpi of the output non-ps plot
312        """
313        self._plotter.save(filename,orientation,dpi)
314        return
315
316
317    def set_mask(self, mask=None, selection=None):
318        """
319        Set a plotting mask for a specific polarization.
320        This is useful for masking out "noise" Pangle outside a source.
321        Parameters:
322             mask:           a mask from scantable.create_mask
323             selection:      the spectra to apply the mask to.
324        Example:
325             select = selector()
326             select.setpolstrings("Pangle")
327             plotter.set_mask(mymask, select)
328        """
329        if not self._data:
330            msg = "Can only set mask after a first call to plot()"
331            if rcParams['verbose']:
332                print msg
333                return
334            else:
335                raise RuntimeError(msg)
336        if len(mask):
337            if isinstance(mask, list) or isinstance(mask, tuple):
338                self._usermask = array(mask)
339            else:
340                self._usermask = mask
341        if mask is None and selection is None:
342            self._usermask = []
343            self._maskselection = None
344        if isinstance(selection, selector):
345            self._maskselection = {'b': selection.get_beams(),
346                                   's': selection.get_scans(),
347                                   'i': selection.get_ifs(),
348                                   'p': selection.get_pols(),
349                                   't': [] }
350        else:
351            self._maskselection = None
352        self.plot(self._data)
353
354    def _slice_indeces(self, data):
355        mn = self._minmaxx[0]
356        mx = self._minmaxx[1]
357        asc = data[0] < data[-1]
358        start=0
359        end = len(data)-1
360        inc = 1
361        if not asc:
362            start = len(data)-1
363            end = 0
364            inc = -1
365        # find min index
366        while data[start] < mn:
367            start+= inc
368        # find max index
369        while data[end] > mx:
370            end-=inc
371        end +=1
372        if start > end:
373            return end,start
374        return start,end
375
376    def _reset(self):
377        self._usermask = []
378        self._usermaskspectra = None
379        self.set_selection(None, False)
380
381    def _plot(self, scan):
382        savesel = scan.get_selection()
383        sel = savesel +  self._selection
384        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
385              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
386        order = [d0[self._panelling],d0[self._stacking]]
387        sel.set_order(order)
388        scan.set_selection(sel)
389        d = {'b': scan.getbeam, 's': scan.getscan,
390             'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
391
392        polmodes = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
393        n,nstack = self._get_selected_n(scan)
394        maxpanel, maxstack = 16,8
395        if n > maxpanel or nstack > maxstack:
396            from asap import asaplog
397            msg ="Scan to be plotted contains more than %d selections.\n" \
398                  "Selecting first %d selections..." % (maxpanel,maxpanel)
399            asaplog.push(msg)
400            print_log()
401            n = min(n,maxpanel)
402            nstack = min(nstack,maxstack)
403
404        if n > 1:
405            ganged = rcParams['plotter.ganged']
406            if self._rows and self._cols:
407                n = min(n,self._rows*self._cols)
408                self._plotter.set_panels(rows=self._rows,cols=self._cols,
409                                         nplots=n,ganged=ganged)
410            else:
411                self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
412        else:
413            self._plotter.set_panels()
414        r=0
415        nr = scan.nrow()
416        a0,b0 = -1,-1
417        allxlim = []
418        allylim = []
419        newpanel=True
420        panelcount,stackcount = 0,0
421        while r < nr:
422            a = d[self._panelling](r)
423            b = d[self._stacking](r)
424            if a > a0 and panelcount < n:
425                if n > 1:
426                    self._plotter.subplot(panelcount)
427                self._plotter.palette(0)
428                #title
429                xlab = self._abcissa and self._abcissa[panelcount] \
430                       or scan._getabcissalabel()
431                ylab = self._ordinate and self._ordinate[panelcount] \
432                       or scan._get_ordinate_label()
433                self._plotter.set_axes('xlabel',xlab)
434                self._plotter.set_axes('ylabel',ylab)
435                lbl = self._get_label(scan, r, self._panelling, self._title)
436                if isinstance(lbl, list) or isinstance(lbl, tuple):
437                    if 0 <= panelcount < len(lbl):
438                        lbl = lbl[panelcount]
439                    else:
440                        # get default label
441                        lbl = self._get_label(scan, r, self._panelling, None)
442                self._plotter.set_axes('title',lbl)
443                newpanel = True
444                stackcount =0
445                panelcount += 1
446            if (b > b0 or newpanel) and stackcount < nstack:
447                y = []
448                if len(polmodes):
449                    y = scan._getspectrum(r, polmodes[scan.getpol(r)])
450                else:
451                    y = scan._getspectrum(r)
452                m = scan._getmask(r)
453                if self._maskselection and len(self._usermask) == len(m):
454                    if d[self._stacking](r) in self._maskselection[self._stacking]:
455                        m = logical_and(m, self._usermask)
456                x = scan._getabcissa(r)
457                if self._minmaxx is not None:
458                    s,e = self._slice_indeces(x)
459                    x = x[s:e]
460                    y = y[s:e]
461                    m = m[s:e]
462                if len(x) > 2048 and rcParams['plotter.decimate']:
463                    fac = len(x)/2048
464                    x = x[::fac]
465                    m = m[::fac]
466                    y = y[::fac]
467                llbl = self._get_label(scan, r, self._stacking, self._lmap)
468                if isinstance(llbl, list) or isinstance(llbl, tuple):
469                    if 0 <= stackcount < len(llbl):
470                        # use user label
471                        llbl = llbl[stackcount]
472                    else:
473                        # get default label
474                        llbl = self._get_label(scan, r, self._stacking, None)
475                self._plotter.set_line(label=llbl)
476                plotit = self._plotter.plot
477                if self._hist: plotit = self._plotter.hist
478                plotit(x,y,m)
479                xlim= self._minmaxx or [min(x),max(x)]
480                allxlim += xlim
481                ylim= self._minmaxy or [min(y),max(y)]
482                allylim += ylim
483                stackcount += 1
484                # last in colour stack -> autoscale x
485                if stackcount == nstack:
486                    allxlim.sort()
487                    self._plotter.axes.set_xlim([allxlim[0],allxlim[-1]])
488                    # clear
489                    allxlim =[]
490
491            newpanel = False
492            a0=a
493            b0=b
494            # ignore following rows
495            if (panelcount == n) and (stackcount == nstack):
496                # last panel -> autoscale y if ganged
497                if rcParams['plotter.ganged']:
498                    allylim.sort()
499                    self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
500                break
501            r+=1 # next row
502        #reset the selector to the scantable's original
503        scan.set_selection(savesel)
504
505    def set_selection(self, selection=None, refresh=True):
506        self._selection = isinstance(selection,selector) and selection or selector()
507        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
508              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
509        order = [d0[self._panelling],d0[self._stacking]]
510        self._selection.set_order(order)
511        if self._data and refresh: self.plot(self._data)
512
513    def _get_selected_n(self, scan):
514        d1 = {'b': scan.nbeam, 's': scan.nscan,
515             'i': scan.nif, 'p': scan.npol, 't': scan.ncycle }
516        d2 = { 'b': len(self._selection.get_beams()),
517               's': len(self._selection.get_scans()),
518               'i': len(self._selection.get_ifs()),
519               'p': len(self._selection.get_pols()),
520               't': len(self._selection.get_cycles()) }
521        n =  d2[self._panelling] or d1[self._panelling]()
522        nstack = d2[self._stacking] or d1[self._stacking]()
523        return n,nstack
524
525    def _get_label(self, scan, row, mode, userlabel=None):
526        pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
527        if len(pms):
528            poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
529        else:
530            poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
531        d = {'b': "Beam "+str(scan.getbeam(row)),
532             's': scan._getsourcename(row),
533             'i': "IF"+str(scan.getif(row)),
534             'p': poleval,
535             't': scan._gettime(row) }
536        return userlabel or d[mode]
Note: See TracBrowser for help on using the repository browser.