source: trunk/python/asapplotter.py @ 920

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

asap2 migration of plotter

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