source: trunk/python/asapplotter.py @ 935

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

re-introduced re-plot

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