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

Last change on this file since 677 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.