source: trunk/python/asapplotter.py @ 1021

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

decimate threshold 1024 -> 2048; added histogram plotting; function documentation updates

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