source: trunk/python/asapplotter.py @ 947

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

made use of selector class, added sort order to _plot command

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 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
40    def _translate(self, instr):
41        keys = "s b i p t".split()
42        if isinstance(instr, str):
43            for key in keys:
44                if instr.lower().startswith(key):
45                    return key
46        return None
47
48    def _newplotter(self):
49        if self._visible:
50            from asap.asaplotgui import asaplotgui as asaplot
51        else:
52            from asap.asaplot import asaplot
53        return asaplot()
54
55
56    def plot(self, scan=None):
57        """
58        Plot a scantable.
59        Parameters:
60            scan:   a scantable
61        Note:
62            If a scantable was specified in a previous call
63            to plot, no argument has to be given to 'replot'
64            NO checking is done that the abcissas of the scantable
65            are consistent e.g. all 'channel' or all 'velocity' etc.
66        """
67        if self._plotter.is_dead:
68            self._plotter = self._newplotter()
69        self._plotter.hold()
70        self._plotter.clear()
71        from asap import scantable
72        if not self._data and not scan:
73            print "please provide a scantable to plot"
74        if isinstance(scan, scantable):
75            if self._data is not None:
76                if scan != self._data:
77                    self._data = scan
78                    # reset
79                    self._reset()
80            else:
81                self._data = scan
82                self._reset()
83        # ranges become invalid when unit changes
84        if self._abcunit and self._abcunit != self._data.get_unit():
85            self._minmaxx = None
86            self._minmaxy = None
87            self._abcunit = self._data.get_unit()
88            self._datamask = None
89        self._plot(self._data)
90        if self._minmaxy is not None:
91            print "setting limits"
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        Example:
222             # two panels are visible on the plotter
223             plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
224        """
225        self._ordinate = ordinate
226        if self._data: self.plot(self._data)
227        return
228
229    def set_abcissa(self, abcissa=None):
230        """
231        Set the x-axis label of the plot. If multiple panels are plotted,
232        multiple labels have to be specified.
233        Example:
234             # two panels are visible on the plotter
235             plotter.set_ordinate(["First X-Axis","Second X-Axis"])
236        """
237        self._abcissa = abcissa
238        if self._data: self.plot(self._data)
239        return
240
241    def set_colors(self, colormap):
242        """
243        Set the colors to be used. The plotter will cycle through
244        these colors when lines are overlaid (stacking mode).
245        Example:
246             plotter.set_colors("red green blue")
247             # If for example four lines are overlaid e.g I Q U V
248             # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
249             # and 'V' will be 'red' again.
250        """
251        if isinstance(colormap,str):
252            colormap = colormap.split()
253        self._plotter.palette(0,colormap=colormap)
254        if self._data: self.plot(self._data)
255
256    def set_linestyles(self, linestyles):
257        """
258        Set the linestyles to be used. The plotter will cycle through
259        these linestyles when lines are overlaid (stacking mode) AND
260        only one color has been set.
261        Parameters:
262             linestyles:     a list of linestyles to use.
263                             'line', 'dashed', 'dotted', 'dashdot',
264                             'dashdotdot' and 'dashdashdot' are
265                             possible
266
267        Example:
268             plotter.set_colors("black")
269             plotter.set_linestyles("line dashed dotted dashdot")
270             # If for example four lines are overlaid e.g I Q U V
271             # 'I' will be 'solid', 'Q' will be 'dashed',
272             # U will be 'dotted' and 'V' will be 'dashdot'.
273        """
274        if isinstance(linestyles,str):
275            linestyles = linestyles.split()
276        self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
277        if self._data: self.plot(self._data)
278
279    def save(self, filename=None, orientation=None, dpi=None):
280        """
281        Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
282        Parameters:
283             filename:    The name of the output file. This is optional
284                          and autodetects the image format from the file
285                          suffix. If non filename is specified a file
286                          called 'yyyymmdd_hhmmss.png' is created in the
287                          current directory.
288             orientation: optional parameter for postscript only (not eps).
289                          'landscape', 'portrait' or None (default) are valid.
290                          If None is choosen for 'ps' output, the plot is
291                          automatically oriented to fill the page.
292             dpi:         The dpi of the output non-ps plot
293        """
294        self._plotter.save(filename,orientation,dpi)
295        return
296
297
298    def set_mask(self, mask=None, selection=None):
299        """
300        Set a plotting mask for a specific polarization.
301        This is useful for masking out "noise" Pangle outside a source.
302        Parameters:
303             mask:           a mask from scantable.create_mask
304             selection:      the spectra to apply the mask to.
305        Example:
306             select = selector()
307             select.setpolstrings("Pangle")
308             plotter.set_mask(mymask, select)
309        """
310        if not self._data:
311            msg = "Can only set mask after a first call to plot()"
312            if rcParams['verbose']:
313                print msg
314                return
315            else:
316                raise RuntimeError(msg)
317        if len(mask):
318            if isinstance(mask, list) or isinstance(mask, tuple):
319                self._usermask = array(mask)
320            else:
321                self._usermask = mask
322        if mask is None and selection is None:
323            self._usermask = []
324            self._maskselection = None
325        if isinstance(selection, selector):
326            self._maskselection = {'b': selection.get_beams(),
327                                   's': selection.get_scans(),
328                                   'i': selection.get_ifs(),
329                                   'p': selection.get_pols(),
330                                   't': [] }
331        else:
332            self._maskselection = None
333        self.plot(self._data)
334
335    def _slice_indeces(self, data):
336        mn = self._minmaxx[0]
337        mx = self._minmaxx[1]
338        asc = data[0] < data[-1]
339        start=0
340        end = len(data)-1
341        inc = 1
342        if not asc:
343            start = len(data)-1
344            end = 0
345            inc = -1
346        # find min index
347        while data[start] < mn:
348            start+= inc
349        # find max index
350        while data[end] > mx:
351            end-=inc
352        end +=1
353        if start > end:
354            return end,start
355        return start,end
356
357    def _reset(self):
358        self._usermask = []
359        self._usermaskspectra = None
360        self.set_selection(None, False)
361
362    def _plot(self, scan):
363        savesel = scan.get_selection()
364        sel = savesel +  self._selection
365        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
366              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
367        order = [d0[self._panelling],d0[self._stacking]]
368        sel.set_order(order)
369        scan.set_selection(sel)
370        d = {'b': scan.getbeam, 's': scan.getscan,
371             'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
372
373        polmodes = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
374        n,nstack = self._get_selected_n(scan)
375        maxpanel, maxstack = 9,4
376        if n > maxpanel or nstack > maxstack:
377            from asap import asaplog
378            msg ="Scan to be plotted contains more than %d selections.\n" \
379                  "Selecting first %d selections..." % (maxpanel,maxpanel)
380            asaplog.push(msg)
381            print_log()
382            n = min(n,maxpanel)
383            nstack = min(n,maxstack)
384
385        if n > 1:
386            ganged = rcParams['plotter.ganged']
387            if self._rows and self._cols:
388                n = min(n,self._rows*self._cols)
389                self._plotter.set_panels(rows=self._rows,cols=self._cols,
390                                         nplots=n,ganged=ganged)
391            else:
392                self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
393        else:
394            self._plotter.set_panels()
395        r=0
396        nr = scan.nrow()
397        a0,b0 = -1,-1
398        allxlim = []
399        newpanel=True
400        panelcount,stackcount = 0,0
401        while r < nr:
402            a = d[self._panelling](r)
403            b = d[self._stacking](r)
404#             print r
405#             print "  a: ",a,a0,panelcount,n
406#             print "  b: ",b,b0,stackcount,nstack
407            if a > a0 and panelcount < n:
408                if n > 1:
409                    self._plotter.subplot(panelcount)
410                    #pindex += 1
411                self._plotter.palette(0)
412                #title
413                xlab = self._abcissa and self._abcissa[panelcount] \
414                       or scan._getabcissalabel()
415                ylab = self._ordinate and self._ordinate[panelcount] \
416                       or scan._get_ordinate_label()
417                self._plotter.set_axes('xlabel',xlab)
418                self._plotter.set_axes('ylabel',ylab)
419                lbl = self._get_label(scan, r, self._panelling, self._title)
420                if isinstance(lbl, list) or isinstance(lbl, tuple):
421                    if 0 <= panelcount < len(lbl):
422                        lbl = lbl[panelcount]
423                    else:
424                        # get default label
425                        lbl = self._get_label(scan, r, self._panelling, None)
426                self._plotter.set_axes('title',lbl)
427                newpanel = True
428                stackcount =0
429                panelcount += 1
430            if (b > b0 or newpanel) and stackcount < nstack:
431                y = []
432                if len(polmodes):
433                    y = scan._getspectrum(r, polmodes[scan.getpol(r)])
434                else:
435                    y = scan._getspectrum(r)
436                m = scan._getmask(r)
437                if self._maskselection and len(self._usermask) == len(m):
438                    if d[self._stacking](r) in self._maskselection[self._stacking]:
439                        print "debug"
440                        m = logical_and(m, self._usermask)
441                x = scan._getabcissa(r)
442                if self._minmaxx is not None:
443                    s,e = self._slice_indeces(x)
444                    x = x[s:e]
445                    y = y[s:e]
446                    m = m[s:e]
447                if len(x) > 1024 and True:#rcParams['plotter.decimate']:
448                    fac = len(x)/1024
449                    x = x[::fac]
450                    m = m[::fac]
451                    y = y[::fac]
452                llbl = self._get_label(scan, r, self._stacking, self._lmap)
453                if isinstance(llbl, list) or isinstance(llbl, tuple):
454                    if 0 <= stackcount < len(llbl):
455                        # use user label
456                        llbl = llbl[stackcount]
457                    else:
458                        # get default label
459                        llbl = self._get_label(scan, r, self._stacking, None)
460                self._plotter.set_line(label=llbl)
461                self._plotter.plot(x,y,m)
462                xlim= self._minmaxx or [min(x),max(x)]
463                allxlim += xlim
464                stackcount += 1
465                # last in colour stack -> autoscale x
466                if stackcount == nstack:
467                    allxlim.sort()
468                    self._plotter.axes.set_xlim([allxlim[0],allxlim[-1]])
469                    # clear
470                    allxlim =[]
471
472            newpanel = False
473            a0=a
474            b0=b
475            # ignore following rows
476            if (panelcount == n) and (stackcount == nstack):
477                pass#break
478            r+=1 # next row
479        #reset the selector to the scantable's original
480        scan.set_selection(savesel)
481
482    def set_selection(self, selection=None, refresh=True):
483        self._selection = isinstance(selection,selector) and selection or selector()
484        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
485              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
486        order = [d0[self._panelling],d0[self._stacking]]
487        self._selection.set_order(order)
488        if self._data and refresh: self.plot(self._data)
489
490    def _get_selected_n(self, scan):
491        d1 = {'b': scan.nbeam, 's': scan.nscan,
492             'i': scan.nif, 'p': scan.npol, 't': scan.ncycle }
493        d2 = { 'b': len(self._selection.get_beams()),
494               's': len(self._selection.get_scans()),
495               'i': len(self._selection.get_ifs()),
496               'p': len(self._selection.get_pols()),
497               't': len(self._selection.get_cycles()) }
498        n =  d2[self._panelling] or d1[self._panelling]()
499        nstack = d2[self._stacking] or d1[self._stacking]()
500        return n,nstack
501
502    def _get_label(self, scan, row, mode, userlabel=None):
503        pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
504        if len(pms):
505            poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
506        else:
507            poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
508        d = {'b': "Beam "+str(scan.getbeam(row)),
509             's': scan._getsourcename(row),
510             'i': "IF"+str(scan.getif(row)),
511             'p': scan.poltype().capitalize()+" "+poleval,
512             't': scan._gettime(row) }
513        return userlabel or d[mode]
Note: See TracBrowser for help on using the repository browser.