source: trunk/python/asapplotter.py @ 1096

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

added legend placing/disable to set_legend. Changed decimate from 2048 to 1024

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.4 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, mode = 0):
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            mode:   where to display the legend
193                    Any other value for loc else disables the legend:
194                        0: auto
195                        1: upper right
196                        2: upper left
197                        3: lower left
198                        4: lower right
199                        5: right
200                        6: center left
201                        7: center right
202                        8: lower center
203                        9: upper center
204                        10: center
205
206        Example:
207             If the data has two IFs/rest frequencies with index 0 and 1
208             for CO and SiO:
209             plotter.set_stacking('i')
210             plotter.set_legend(['CO','SiO'])
211             plotter.plot()
212             plotter.set_legend([r'$^{12}CO$', r'SiO'])
213        """
214        self._lmap = mp
215        self._plotter.legend(mode)
216        if self._data:
217            self.plot(self._data)
218        return
219
220    def set_title(self, title=None):
221        """
222        Set the title of the plot. If multiple panels are plotted,
223        multiple titles have to be specified.
224        Example:
225             # two panels are visible on the plotter
226             plotter.set_title(["First Panel","Second Panel"])
227        """
228        self._title = title
229        if self._data: self.plot(self._data)
230        return
231
232    def set_ordinate(self, ordinate=None):
233        """
234        Set the y-axis label of the plot. If multiple panels are plotted,
235        multiple labels have to be specified.
236        Parameters:
237            ordinate:    a list of ordinate labels. None (default) let
238                         data determine the labels
239        Example:
240             # two panels are visible on the plotter
241             plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
242        """
243        self._ordinate = ordinate
244        if self._data: self.plot(self._data)
245        return
246
247    def set_abcissa(self, abcissa=None):
248        """
249        Set the x-axis label of the plot. If multiple panels are plotted,
250        multiple labels have to be specified.
251        Parameters:
252            abcissa:     a list of abcissa labels. None (default) let
253                         data determine the labels
254        Example:
255             # two panels are visible on the plotter
256             plotter.set_ordinate(["First X-Axis","Second X-Axis"])
257        """
258        self._abcissa = abcissa
259        if self._data: self.plot(self._data)
260        return
261
262    def set_colors(self, colormap):
263        """
264        Set the colors to be used. The plotter will cycle through
265        these colors when lines are overlaid (stacking mode).
266        Parameters:
267            colormap:     a list of colour names
268        Example:
269             plotter.set_colors("red green blue")
270             # If for example four lines are overlaid e.g I Q U V
271             # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
272             # and 'V' will be 'red' again.
273        """
274        if isinstance(colormap,str):
275            colormap = colormap.split()
276        self._plotter.palette(0,colormap=colormap)
277        if self._data: self.plot(self._data)
278
279    def set_histogram(self, hist=True):
280        """
281        Enable/Disable histogram-like plotting.
282        Parameters:
283            hist:        True (default) or False. The fisrt default
284                         is taken from the .asaprc setting
285                         plotter.histogram
286        """
287        self._hist = hist
288        if self._data: self.plot(self._data)
289
290    def set_linestyles(self, linestyles):
291        """
292        Set the linestyles to be used. The plotter will cycle through
293        these linestyles when lines are overlaid (stacking mode) AND
294        only one color has been set.
295        Parameters:
296             linestyles:     a list of linestyles to use.
297                             'line', 'dashed', 'dotted', 'dashdot',
298                             'dashdotdot' and 'dashdashdot' are
299                             possible
300
301        Example:
302             plotter.set_colors("black")
303             plotter.set_linestyles("line dashed dotted dashdot")
304             # If for example four lines are overlaid e.g I Q U V
305             # 'I' will be 'solid', 'Q' will be 'dashed',
306             # U will be 'dotted' and 'V' will be 'dashdot'.
307        """
308        if isinstance(linestyles,str):
309            linestyles = linestyles.split()
310        self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
311        if self._data: self.plot(self._data)
312
313    def save(self, filename=None, orientation=None, dpi=None):
314        """
315        Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
316        Parameters:
317             filename:    The name of the output file. This is optional
318                          and autodetects the image format from the file
319                          suffix. If non filename is specified a file
320                          called 'yyyymmdd_hhmmss.png' is created in the
321                          current directory.
322             orientation: optional parameter for postscript only (not eps).
323                          'landscape', 'portrait' or None (default) are valid.
324                          If None is choosen for 'ps' output, the plot is
325                          automatically oriented to fill the page.
326             dpi:         The dpi of the output non-ps plot
327        """
328        self._plotter.save(filename,orientation,dpi)
329        return
330
331
332    def set_mask(self, mask=None, selection=None):
333        """
334        Set a plotting mask for a specific polarization.
335        This is useful for masking out "noise" Pangle outside a source.
336        Parameters:
337             mask:           a mask from scantable.create_mask
338             selection:      the spectra to apply the mask to.
339        Example:
340             select = selector()
341             select.setpolstrings("Pangle")
342             plotter.set_mask(mymask, select)
343        """
344        if not self._data:
345            msg = "Can only set mask after a first call to plot()"
346            if rcParams['verbose']:
347                print msg
348                return
349            else:
350                raise RuntimeError(msg)
351        if len(mask):
352            if isinstance(mask, list) or isinstance(mask, tuple):
353                self._usermask = array(mask)
354            else:
355                self._usermask = mask
356        if mask is None and selection is None:
357            self._usermask = []
358            self._maskselection = None
359        if isinstance(selection, selector):
360            self._maskselection = {'b': selection.get_beams(),
361                                   's': selection.get_scans(),
362                                   'i': selection.get_ifs(),
363                                   'p': selection.get_pols(),
364                                   't': [] }
365        else:
366            self._maskselection = None
367        self.plot(self._data)
368
369    def _slice_indeces(self, data):
370        mn = self._minmaxx[0]
371        mx = self._minmaxx[1]
372        asc = data[0] < data[-1]
373        start=0
374        end = len(data)-1
375        inc = 1
376        if not asc:
377            start = len(data)-1
378            end = 0
379            inc = -1
380        # find min index
381        while data[start] < mn:
382            start+= inc
383        # find max index
384        while data[end] > mx:
385            end-=inc
386        end +=1
387        if start > end:
388            return end,start
389        return start,end
390
391    def _reset(self):
392        self._usermask = []
393        self._usermaskspectra = None
394        self.set_selection(None, False)
395
396    def _plot(self, scan):
397        savesel = scan.get_selection()
398        sel = savesel +  self._selection
399        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
400              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
401        order = [d0[self._panelling],d0[self._stacking]]
402        sel.set_order(order)
403        scan.set_selection(sel)
404        d = {'b': scan.getbeam, 's': scan.getscan,
405             'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
406
407        polmodes = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
408        n,nstack = self._get_selected_n(scan)
409        maxpanel, maxstack = 16,8
410        if n > maxpanel or nstack > maxstack:
411            from asap import asaplog
412            msg ="Scan to be plotted contains more than %d selections.\n" \
413                  "Selecting first %d selections..." % (maxpanel,maxpanel)
414            asaplog.push(msg)
415            print_log()
416            n = min(n,maxpanel)
417            nstack = min(nstack,maxstack)
418
419        if n > 1:
420            ganged = rcParams['plotter.ganged']
421            if self._rows and self._cols:
422                n = min(n,self._rows*self._cols)
423                self._plotter.set_panels(rows=self._rows,cols=self._cols,
424                                         nplots=n,ganged=ganged)
425            else:
426                self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
427        else:
428            self._plotter.set_panels()
429        r=0
430        nr = scan.nrow()
431        a0,b0 = -1,-1
432        allxlim = []
433        allylim = []
434        newpanel=True
435        panelcount,stackcount = 0,0
436        while r < nr:
437            a = d[self._panelling](r)
438            b = d[self._stacking](r)
439            if a > a0 and panelcount < n:
440                if n > 1:
441                    self._plotter.subplot(panelcount)
442                self._plotter.palette(0)
443                #title
444                xlab = self._abcissa and self._abcissa[panelcount] \
445                       or scan._getabcissalabel()
446                ylab = self._ordinate and self._ordinate[panelcount] \
447                       or scan._get_ordinate_label()
448                self._plotter.set_axes('xlabel',xlab)
449                self._plotter.set_axes('ylabel',ylab)
450                lbl = self._get_label(scan, r, self._panelling, self._title)
451                if isinstance(lbl, list) or isinstance(lbl, tuple):
452                    if 0 <= panelcount < len(lbl):
453                        lbl = lbl[panelcount]
454                    else:
455                        # get default label
456                        lbl = self._get_label(scan, r, self._panelling, None)
457                self._plotter.set_axes('title',lbl)
458                newpanel = True
459                stackcount =0
460                panelcount += 1
461            if (b > b0 or newpanel) and stackcount < nstack:
462                y = []
463                if len(polmodes):
464                    y = scan._getspectrum(r, polmodes[scan.getpol(r)])
465                else:
466                    y = scan._getspectrum(r)
467                m = scan._getmask(r)
468                if self._maskselection and len(self._usermask) == len(m):
469                    if d[self._stacking](r) in self._maskselection[self._stacking]:
470                        m = logical_and(m, self._usermask)
471                x = scan._getabcissa(r)
472                from matplotlib.numerix import ma,logical_not,array
473                y = ma.MA.MaskedArray(y,mask=logical_not(array(m,copy=0)),copy=0)
474                if self._minmaxx is not None:
475                    s,e = self._slice_indeces(x)
476                    x = x[s:e]
477                    y = y[s:e]
478                if len(x) > 1024 and rcParams['plotter.decimate']:
479                    fac = len(x)/1024
480                    x = x[::fac]
481                    y = y[::fac]
482                llbl = self._get_label(scan, r, self._stacking, self._lmap)
483                if isinstance(llbl, list) or isinstance(llbl, tuple):
484                    if 0 <= stackcount < len(llbl):
485                        # use user label
486                        llbl = llbl[stackcount]
487                    else:
488                        # get default label
489                        llbl = self._get_label(scan, r, self._stacking, None)
490                self._plotter.set_line(label=llbl)
491                plotit = self._plotter.plot
492                if self._hist: plotit = self._plotter.hist
493                plotit(x,y)
494                xlim= self._minmaxx or [min(x),max(x)]
495                allxlim += xlim
496                ylim= self._minmaxy or [ma.MA.minimum(y),ma.MA.maximum(y)]
497                allylim += ylim
498                stackcount += 1
499                # last in colour stack -> autoscale x
500                if stackcount == nstack:
501                    allxlim.sort()
502                    self._plotter.axes.set_xlim([allxlim[0],allxlim[-1]])
503                    # clear
504                    allxlim =[]
505
506            newpanel = False
507            a0=a
508            b0=b
509            # ignore following rows
510            if (panelcount == n) and (stackcount == nstack):
511                # last panel -> autoscale y if ganged
512                if rcParams['plotter.ganged']:
513                    allylim.sort()
514                    self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
515                break
516            r+=1 # next row
517        #reset the selector to the scantable's original
518        scan.set_selection(savesel)
519
520    def set_selection(self, selection=None, refresh=True):
521        self._selection = isinstance(selection,selector) and selection or selector()
522        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
523              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
524        order = [d0[self._panelling],d0[self._stacking]]
525        self._selection.set_order(order)
526        if self._data and refresh: self.plot(self._data)
527
528    def _get_selected_n(self, scan):
529        d1 = {'b': scan.nbeam, 's': scan.nscan,
530             'i': scan.nif, 'p': scan.npol, 't': scan.ncycle }
531        d2 = { 'b': len(self._selection.get_beams()),
532               's': len(self._selection.get_scans()),
533               'i': len(self._selection.get_ifs()),
534               'p': len(self._selection.get_pols()),
535               't': len(self._selection.get_cycles()) }
536        n =  d2[self._panelling] or d1[self._panelling]()
537        nstack = d2[self._stacking] or d1[self._stacking]()
538        return n,nstack
539
540    def _get_label(self, scan, row, mode, userlabel=None):
541        pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
542        if len(pms):
543            poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
544        else:
545            poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
546        d = {'b': "Beam "+str(scan.getbeam(row)),
547             's': scan._getsourcename(row),
548             'i': "IF"+str(scan.getif(row)),
549             'p': poleval,
550             't': scan._gettime(row) }
551        return userlabel or d[mode]
Note: See TracBrowser for help on using the repository browser.