source: branches/Release-2-fixes/python/asapplotter.py @ 672

Last change on this file since 672 was 672, checked in by mar637, 19 years ago
  • Bug fix: set_cursor for Polarisations wasn't working for e.g. "U V"
  • data slicing on set_range to give mpl only the range selected. mpl ps backend always drwas all points and the clips (known bug)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.5 KB
Line 
1from asap.asaplot import ASAPlot
2from asap import rcParams
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):
14        self._plotter = ASAPlot()
15
16        self._tdict = {'Time':'t','time':'t','t':'t','T':'t'}
17        self._bdict = {'Beam':'b','beam':'b','b':'b','B':'b'}
18        self._idict = {'IF':'i','if':'i','i':'i','I':'i'}
19        self._pdict = {'Pol':'p','pol':'p','p':'p'}
20        self._sdict = {'scan':'s','Scan':'s','s':'s','S':'s'}
21        self._cdict = {'t':'len(self._cursor["t"])',
22                       'b':'len(self._cursor["b"])',
23                       'i':'len(self._cursor["i"])',
24                       'p':'len(self._cursor["p"])',
25                       's':'len(scans)'}
26        self._ldict = {'b':'Beam',
27                       'i':'IF',
28                       'p':'Pol',
29                       's':'Scan'}
30        self._dicts = [self._tdict,self._bdict,
31                       self._idict,self._pdict,
32                       self._sdict]
33        self._panelling = None
34        self._stacking = None
35        self.set_panelling()
36        self.set_stacking()
37        self._rows = None
38        self._cols = None
39        self._autoplot = False
40        self._minmaxx = None
41        self._minmaxy = None
42        self._data = None
43        self._lmap = None
44        self._title = None
45        self._ordinate = None
46        self._abcissa = None
47        self._abcunit = None
48        self._cursor = {'t':None, 'b':None,
49                        'i':None, 'p':None
50                        }
51
52    def _translate(self, name):
53        for d in self._dicts:
54            if d.has_key(name):
55                return d[name]
56        return None
57       
58    def plot(self, *args):
59        """
60        Plot a (list of) scantables.
61        Parameters:
62            one or more comma separated scantables
63        Note:
64            If a (list) of scantables was specified in a previous call
65            to plot, no argument has to be given to 'replot'
66            NO checking is done that the abcissas of the scantables
67            are consistent e.g. all 'channel' or all 'velocity' etc.
68        """
69        if self._plotter.is_dead:
70            self._plotter = ASAPlot()
71        self._plotter.hold()
72        self._plotter.clear()
73        if len(args) > 0:
74            if self._data is not None:               
75                if list(args) != self._data:
76                    self._data = list(args)
77                    # reset cursor
78                    self.set_cursor(refresh=False)
79            else:
80                self._data = list(args)
81                self.set_cursor(refresh=False)
82        # ranges become invalid when unit changes
83        if self._abcunit != self._data[0].get_unit():
84            self._minmaxx = None
85            self._minmaxy = None
86            self._abcunit = self._data[0].get_unit()
87        if self._panelling == 't':
88            maxrows = 25
89            if self._data[0].nrow() > maxrows:
90                if self._cursor["t"] is None or \
91                       (isinstance(self._cursor["t"],list) and \
92                        len(self._cursor["t"]) > maxrows ):
93                    print "Scan to be plotted contains more than %d rows.\n" \
94                          "Selecting first %d rows..." % (maxrows,maxrows)
95                    self._cursor["t"] = range(maxrows)
96            self._plot_time(self._data[0], self._stacking)
97        elif self._panelling == 's':
98            self._plot_scans(self._data, self._stacking)
99        else:
100            self._plot_other(self._data, self._stacking)
101        if self._minmaxy is not None:
102            self._plotter.set_limits(ylim=self._minmaxy)
103        self._plotter.release()
104        return
105
106    def _plot_time(self, scan, colmode):
107        if colmode == 't':
108            return
109        n = len(self._cursor["t"])
110        cdict = {'b':'scan.setbeam(j)',
111                 'i':'scan.setif(j)',
112                 'p':'scan.setpol(j)'}
113        cdict2 = {'b':'self._cursor["b"]',
114                  'i':'self._cursor["i"]',
115                  'p':'self._cursor["p"]'}
116        ncol = 1
117        if self._stacking is not None:
118            ncol = eval(self._cdict.get(colmode))
119        if n > 1:
120            if self._rows and self._cols:
121                n = min(n,self._rows*self._cols)
122                self._plotter.set_panels(rows=self._rows,cols=self._cols,
123                                         nplots=n)
124            else:
125                self._plotter.set_panels(rows=n,cols=0,nplots=n)
126        else:
127            self._plotter.set_panels()
128        rows = self._cursor["t"]
129        self._plotter.palette(0)
130        for rowsel in rows:
131            i = self._cursor["t"].index(rowsel)
132            if n > 1:
133                self._plotter.palette(0)
134                self._plotter.subplot(i)
135            colvals = eval(cdict2.get(colmode))
136            for j in colvals:
137                polmode = "raw"
138                jj = colvals.index(j)
139                savej = j
140                for k in cdict.keys():
141                    sel = eval(cdict2.get(k))                   
142                    j = sel[0]
143                    if k == "p":
144                        which = self._cursor["p"].index(j)
145                        polmode = self._polmode[which]
146                        j = which
147                    eval(cdict.get(k))
148                j = savej
149                if colmode == "p":
150                    polmode = self._polmode[self._cursor["p"].index(j)]
151                    #j = jj
152                eval(cdict.get(colmode))
153                x = None
154                y = None
155                m = None
156                if self._title is None:
157                    tlab = scan._getsourcename(rowsel)                   
158                else:
159                    if len(self._title) >= n:
160                        tlab = self._title[rowsel]
161                    else:
162                        tlab = scan._getsourcename(rowsel)
163                x,xlab = scan.get_abcissa(rowsel)
164                if self._abcissa: xlab = self._abcissa
165                y = None
166                if polmode == "stokes":
167                    y = scan._getstokesspectrum(rowsel)
168                elif polmode == "stokes2":
169                    y = scan._getstokesspectrum(rowsel,True)
170                elif polmode == "circular":
171                    y = scan._stokestopolspectrum(rowsel,False,-1)
172                else:
173                    y = scan._getspectrum(rowsel)
174                if self._ordinate:
175                    ylab = self._ordinate
176                else:
177                    ylab = scan._get_ordinate_label()
178                m = scan._getmask(rowsel)
179                if self._lmap and len(self._lmap) > 0:
180                    llab = self._lmap[jj]
181                else:
182                    if colmode == 'p':
183                        llab = self._get_pollabel(scan, polmode)
184                    else:                   
185                        llab = self._ldict.get(colmode)+' '+str(j)
186                self._plotter.set_line(label=llab)
187                if self._minmaxx is not None:
188                    s,e = self._slice_indeces(x)
189                    x = x[s:e]
190                    y = y[s:e]
191                    m = m[s:e]
192                self._plotter.plot(x,y,m)
193               
194                xlim=[min(x),max(x)]
195                self._plotter.axes.set_xlim(xlim)
196            self._plotter.set_axes('xlabel',xlab)
197            self._plotter.set_axes('ylabel',ylab)
198            self._plotter.set_axes('title',tlab)           
199        return
200
201    def _plot_scans(self, scans, colmode):
202        print "Can only plot one row per scan."
203        if colmode == 's':
204            return
205        cdict = {'b':'scan.setbeam(j)',
206                 'i':'scan.setif(j)',
207                 'p':'scan.setpol(j)'}
208        cdict2 = {'b':'self._cursor["b"]',
209                  'i':'self._cursor["i"]',
210                  'p':'self._cursor["p"]'}
211       
212        n = len(scans)
213        ncol = 1
214        if self._stacking is not None:
215            scan = scans[0]
216            ncol = eval(self._cdict.get(colmode))
217        if n > 1:
218            if self._rows and self._cols:
219                n = min(n,self._rows*self._cols)
220                self._plotter.set_panels(rows=self._rows,cols=self._cols,
221                                         nplots=n)
222            else:
223                self._plotter.set_panels(rows=n,cols=0,nplots=n)
224        else:
225            self._plotter.set_panels()
226
227        for scan in scans:
228            self._plotter.palette(0)
229            if n > 1:
230                self._plotter.subplot(scans.index(scan))
231            colvals = eval(cdict2.get(colmode))
232            rowsel = self._cursor["t"][0]
233            for j in colvals:
234                polmode = "raw"
235                jj = colvals.index(j)
236                savej = j
237                for k in cdict.keys():
238                    sel = eval(cdict2.get(k))                   
239                    j = sel[0]
240                    eval(cdict.get(k))
241                    if k == "p":
242                        which = self._cursor["p"].index(j)
243                        polmode = self._polmode[which]
244                        j = which
245                j = savej
246                if colmode == "p":
247                    polmode = self._polmode[self._cursor["p"].index(j)]
248                    #j = jj
249                eval(cdict.get(colmode))
250                x = None
251                y = None
252                m = None
253                tlab = self._title
254                if not self._title:
255                    tlab = scan._getsourcename(rowsel)
256                x,xlab = scan.get_abcissa(rowsel)
257                if self._abcissa: xlab = self._abcissa
258                if polmode == "stokes":
259                    y = scan._getstokesspectrum(rowsel)
260                elif polmode == "stokes2":
261                    y = scan._getstokesspectrum(rowsel,True)
262                elif polmode == "circular":
263                    y = scan._stokestopolspectrum(rowsel,False,-1)
264                else:
265                    y = scan._getspectrum(rowsel)
266                if self._ordinate:
267                    ylab = self._ordinate
268                else:
269                    ylab = scan._get_ordinate_label()
270                m = scan._getmask(rowsel)
271                if self._lmap and len(self._lmap) > 0:
272                    llab = self._lmap[jj]
273                else:
274                    if colmode == 'p':
275                        llab = self._get_pollabel(scan, polmode)
276                    else:
277                        llab = self._ldict.get(colmode)+' '+str(j)
278                self._plotter.set_line(label=llab)
279                if self._minmaxx is not None:
280                    s,e = self._slice_indeces(x)
281                    x = x[s:e]
282                    y = y[s:e]
283                    m = m[s:e]
284
285                self._plotter.plot(x,y,m)
286                xlim=[min(x),max(x)]
287                self._plotter.axes.set_xlim(xlim)
288
289            self._plotter.set_axes('xlabel',xlab)
290            self._plotter.set_axes('ylabel',ylab)
291            self._plotter.set_axes('title',tlab)
292        return
293   
294    def _plot_other(self,scans,colmode):
295        if colmode == self._panelling:
296            return
297        cdict = {'b':'scan.setbeam(i)',
298                 'i':'scan.setif(i)',
299                 'p':'scan.setpol(i)'}
300        cdict2 = {'b':'self._cursor["b"]',
301                  'i':'self._cursor["i"]',
302                  'p':'self._cursor["p"]',
303                  's': 'scans',
304                  't': 'self._cursor["t"]'}
305        scan = scans[0]
306        n = eval(self._cdict.get(self._panelling))
307        ncol=1
308        if self._stacking is not None:           
309            ncol = eval(self._cdict.get(colmode))
310        if n > 1:
311            if self._rows and self._cols:
312                n = min(n,self._rows*self._cols)
313                self._plotter.set_panels(rows=self._rows,cols=self._cols,
314                                         nplots=n)
315            else:
316                self._plotter.set_panels(rows=n,cols=0,nplots=n)
317        else:
318            self._plotter.set_panels()           
319        panels = self._cursor[self._panelling]       
320        for i in panels:
321            self._plotter.palette(0)
322            polmode = "raw"
323            ii = self._cursor[self._panelling].index(i)
324            if n>1:
325                self._plotter.subplot(ii)
326            if self._panelling == "p":
327                polmode = self._polmode[ii]
328                eval(cdict.get(self._panelling))
329            else:
330                eval(cdict.get(self._panelling))
331            colvals = eval(cdict2.get(colmode))
332            for j in colvals:
333                rowsel = self._cursor["t"][0]
334                jj = colvals.index(j)
335                savei = i
336                for k in cdict.keys():
337                    if k != self._panelling:
338                        sel = eval(cdict2.get(k))
339                        i = sel[0]
340                        if k == "p":
341                            which = self._cursor["p"].index(i)
342                            polmode = self._polmode[which]
343                            i = which                       
344                        eval(cdict.get(k))
345                i = savei
346                if colmode == 's':
347                    scan = j
348                elif colmode == 't':
349                    rowsel = j                   
350                else:
351                    savei = i
352                    if colmode == 'p':
353                        polmode = self._polmode[self._cursor["p"].index(j)]
354                    i = j
355                    eval(cdict.get(colmode))
356                    i = savei
357                x = None
358                y = None
359                m = None
360                x,xlab = scan.get_abcissa(rowsel)
361                if self._abcissa: xlab = self._abcissa
362                if polmode == "stokes":
363                    y = scan._getstokesspectrum(rowsel)
364                elif polmode == "stokes2":
365                    y = scan._getstokesspectrum(rowsel,True)
366                elif polmode == "circular":
367                    y = scan._stokestopolspectrum(rowsel,False,-1)
368                else:
369                    y = scan._getspectrum(rowsel)
370
371                if self._ordinate:
372                    ylab = self._ordinate
373                else:
374                    ylab = scan._get_ordinate_label()
375                m = scan._getmask(rowsel)
376                if colmode == 's' or colmode == 't':
377                    if self._title and len(self._title) > 0:
378                        tlab = self._title[ii]
379                    else:
380                        if self._panelling == 'p':
381                            tlab = self._get_pollabel(scan, polmode)
382                        else:
383                            tlab = self._ldict.get(self._panelling)+' '+str(i)
384                    if self._lmap and len(self._lmap) > 0:
385                        llab = self._lmap[jj]
386                    else:
387                        llab = scan._getsourcename(rowsel)
388                else:
389                    if self._title and len(self._title) > 0:
390                        tlab = self._title[ii]
391                    else:
392                        if self._panelling == 'p':
393                            tlab = self._get_pollabel(scan, polmode)
394                        else:
395                            tlab = self._ldict.get(self._panelling)+' '+str(i)
396                    if self._lmap and len(self._lmap) > 0:
397                        llab = self._lmap[jj]
398                    else:
399                        if colmode == 'p':
400                            llab = self._get_pollabel(scan, polmode)
401                        else:
402                            llab = self._ldict.get(colmode)+' '+str(j)
403                self._plotter.set_line(label=llab)
404                if self._minmaxx is not None:
405                    s,e = self._slice_indeces(x)
406                    x = x[s:e]
407                    y = y[s:e]
408                    m = m[s:e]
409
410                self._plotter.plot(x,y,m)
411                xlim=[min(x),max(x)]
412                self._plotter.axes.set_xlim(xlim)
413
414            self._plotter.set_axes('xlabel',xlab)
415            self._plotter.set_axes('ylabel',ylab)
416            self._plotter.set_axes('title',tlab)
417       
418        return
419
420
421    def set_mode(self, stacking=None, panelling=None):
422        """
423        Set the plots look and feel, i.e. what you want to see on the plot.
424        Parameters:
425            stacking:     tell the plotter which variable to plot
426                          as line colour overlays (default 'pol')
427            panelling:    tell the plotter which variable to plot
428                          across multiple panels (default 'scan'
429        Note:
430            Valid modes are:
431                 'beam' 'Beam' 'b':     Beams
432                 'if' 'IF' 'i':         IFs
433                 'pol' 'Pol' 'p':       Polarisations
434                 'scan' 'Scan' 's':     Scans
435                 'time' 'Time' 't':     Times
436        """
437        if not self.set_panelling(panelling):
438            print "Invalid mode"
439            return
440        if not self.set_stacking(stacking):
441            print "Invalid mode"
442            return
443        if self._data: self.plot()
444        return
445
446    def set_panelling(self, what=None):
447        mode = what
448        if mode is None:
449             mode = rcParams['plotter.panelling']
450        md = self._translate(mode)
451        if md:
452            self._panelling = md
453            self._title = None
454            return True
455        return False
456
457    def set_layout(self,rows=None,cols=None):
458        """
459        Set the multi-panel layout, i.e. how many rows and columns plots
460        are visible.
461        Parameters:
462             rows:   The number of rows of plots
463             cols:   The number of columns of plots
464        Note:
465             If no argument is given, the potter reverts to its auto-plot
466             behaviour.
467        """
468        self._rows = rows
469        self._cols = cols
470        if self._data: self.plot()
471        return
472
473    def set_stacking(self, what=None): 
474        mode = what
475        if mode is None:           
476             mode = rcParams['plotter.stacking']       
477        md = self._translate(mode)
478        if md:
479            self._stacking = md
480            self._lmap = None
481            return True
482        return False
483
484    def set_range(self,xstart=None,xend=None,ystart=None,yend=None):
485        """
486        Set the range of interest on the abcissa of the plot
487        Parameters:
488            [x,y]start,[x,y]end:  The start and end points of the 'zoom' window
489        Note:
490            These become non-sensical when the unit changes.
491            use plotter.set_range() without parameters to reset
492
493        """
494        if xstart is None and xend is None:
495            self._minmaxx = None
496        else:
497            self._minmaxx = [xstart,xend]
498        if ystart is None and yend is None:
499            self._minmaxy = None
500        else:
501            self._minmaxy = [ystart,yend]           
502        if self._data: self.plot()
503        return
504   
505    def set_legend(self, mp=None):
506        """
507        Specify a mapping for the legend instead of using the default
508        indices:
509        Parameters:
510             mp:    a list of 'strings'. This should have the same length
511                    as the number of elements on the legend and then maps
512                    to the indeces in order
513
514        Example:
515             If the data has two IFs/rest frequencies with index 0 and 1
516             for CO and SiO:
517             plotter.set_stacking('i')
518             plotter.set_legend_map(['CO','SiO'])
519             plotter.plot()
520        """
521        self._lmap = mp
522        if self._data: self.plot()
523        return
524
525    def set_title(self, title=None):
526        self._title = title
527        if self._data: self.plot()
528        return
529
530    def set_ordinate(self, ordinate=None):
531        self._ordinate = ordinate
532        if self._data: self.plot()
533        return
534
535    def set_abcissa(self, abcissa=None):
536        self._abcissa = abcissa
537        if self._data: self.plot()
538        return
539
540    def save(self, filename=None, orientation=None):
541        """
542        Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
543        Parameters:
544             filename:    The name of the output file. This is optional
545                          and autodetects the image format from the file
546                          suffix. If non filename is specified a file
547                          called 'yyyymmdd_hhmmss.png' is created in the
548                          current directory.
549             orientation: optional parameter for postscript only (not eps).
550                          'landscape', 'portrait' or None (default) are valid.
551                          If None is choosen for 'ps' output, the plot is
552                          automatically oriented to fill the page.
553        """
554        self._plotter.save(filename,orientation)
555        return
556   
557    def set_cursor(self, row=None,beam=None,IF=None,pol=None, refresh=True):
558        """
559        Specify a 'cursor' for plotting selected spectra. Time (rows),
560        Beam, IF, Polarisation ranges can be specified.
561        Parameters:
562            Default for all paramaters is to select all available
563            row:    selects the rows (time stamps) to be plotted, this has
564                    to be a vector of row indices, e.g. row=[0,2,5] or row=[2]
565            beam:   select a range of beams
566            IF:     select a range of IFs
567            pol:    select Polarisations for plotting these can be by index
568                    (raw polarisations (default)) or by names any of:
569                    ["I", "Q", "U", "V"] or
570                    ["I", "Plinear", "Pangle", "V"] or
571                    ["XX", "YY", "Real(XY)", "Imag(XY)"] or
572                    ["RR", "LL"]
573        Example:
574            plotter.set_mode('pol','time')
575            plotter.plot(myscan) # plots all raw polarisations colour stacked
576            plotter.set_cursor(pol=["I"]) # plot "I" only for all rows
577            # plot "I" only for two time stamps row=0 and row=2
578            plotter.set_cursor(row=[0,2],pol=["I"])
579
580        Note:
581            Be careful to select only exisiting polarisations.           
582        """
583        if not self._data:
584            print "Can only set cursor after a first call to plot()"
585            return
586       
587        n = self._data[0].nrow()
588        if row is None:
589            self._cursor["t"] = range(n)
590        else:
591            for i in row:
592                if i < 0 or i >= n:
593                    print "Row index '%d' out of range" % i
594                    return
595            self._cursor["t"] = row
596
597        n = self._data[0].nbeam()
598        if beam is None:
599            self._cursor["b"] = range(n)
600        else:
601            for i in beam:
602                if i < 0 or  i >= n:
603                    print "Beam index '%d' out of range" % i
604                    return           
605            self._cursor["b"] = beam
606
607        n = self._data[0].nif()
608        if IF is None:
609            self._cursor["i"] = range(n)
610        else:
611            for i in IF:
612                if i < 0 or i >= n:
613                    print "IF index '%d' out of range" %i
614                    return           
615            self._cursor["i"] = IF           
616
617        n = self._data[0].npol()
618        dstokes = {"I":0,"Q":1,"U":2,"V":3}
619        dstokes2 = {"I":0,"Plinear":1,"Pangle":2,"V":3}
620        draw = {"XX":0, "YY":1,"Real(XY)":2, "Imag(XY)":3}
621        dcirc = { "RR":0,"LL":1}#,"Real(RL)":2,"Image(RL)":3}
622       
623        if pol is None:
624            self._cursor["p"] = range(n)
625            self._polmode = ["raw" for i in range(n)]
626        else:
627            if isinstance(pol,str):
628                pol = pol.split()
629            polmode = []
630            pols = []
631            for i in pol:
632                if isinstance(i,str):
633                    if draw.has_key(i):
634                        pols.append(draw.get(i))
635                        polmode.append("raw")
636                    elif dstokes.has_key(i):
637                        pols.append(dstokes.get(i))
638                        polmode.append("stokes")
639                    elif dstokes2.has_key(i):
640                        pols.append(dstokes2.get(i))
641                        polmode.append("stokes2")
642                    elif dcirc.has_key(i):
643                        pols.append(dcirc.get(i))
644                        polmode.append("circular")
645                    else:
646                        print "Pol type '%s' not valid" %i
647                        return
648                elif 0 > i >= n:
649                    print "Pol index '%d' out of range" %i
650                    return
651                else:
652                    pols.append(i)
653                    polmode.append("raw")
654            self._cursor["p"] = pols
655            self._polmode = polmode
656        if self._data and refresh: self.plot()
657
658    def _get_pollabel(self, scan, polmode):
659        tlab = ""
660        if polmode == "stokes":
661            tlab = scan._getpolarizationlabel(0,1,0)
662        elif polmode == "stokes2":
663            tlab = scan._getpolarizationlabel(0,1,1)
664        elif polmode == "circular":
665            tlab = scan._getpolarizationlabel(0,0,0)
666        else:
667            tlab = scan._getpolarizationlabel(1,0,0)
668        return tlab
669
670    def _slice_indeces(self, data):
671        mn = self._minmaxx[0]
672        mx = self._minmaxx[1]
673        asc = data[0] < data[-1]       
674        start=0
675        end = len(data)-1
676        inc = 1
677        if not asc:
678            start = len(data)-1
679            end = 0
680            inc = -1
681        # find min index
682        while data[start] < mn:
683            start+= inc
684        # find max index
685        while data[end] > mx:
686            end-=inc
687        end +=1
688        if start > end:
689            return end,start
690        return start,end
691           
692if __name__ == '__main__':
693    plotter = asapplotter()
Note: See TracBrowser for help on using the repository browser.