source: trunk/python/asapplotter.py@ 972

Last change on this file since 972 was 964, checked in by mar637, 19 years ago

removed type prefix for polarisation labels

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 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
40 def _translate(self, instr):
41 keys = "s b i p t".split()
42 if isinstance(instr, str):
43 for key in keys:
44 if instr.lower().startswith(key):
45 return key
46 return None
47
48 def _newplotter(self):
49 if self._visible:
50 from asap.asaplotgui import asaplotgui as asaplot
51 else:
52 from asap.asaplot import asaplot
53 return asaplot()
54
55
56 def plot(self, scan=None):
57 """
58 Plot a scantable.
59 Parameters:
60 scan: a scantable
61 Note:
62 If a scantable was specified in a previous call
63 to plot, no argument has to be given to 'replot'
64 NO checking is done that the abcissas of the scantable
65 are consistent e.g. all 'channel' or all 'velocity' etc.
66 """
67 if self._plotter.is_dead:
68 self._plotter = self._newplotter()
69 self._plotter.hold()
70 self._plotter.clear()
71 from asap import scantable
72 if not self._data and not scan:
73 print "please provide a scantable to plot"
74 if isinstance(scan, scantable):
75 if self._data is not None:
76 if scan != self._data:
77 self._data = scan
78 # reset
79 self._reset()
80 else:
81 self._data = scan
82 self._reset()
83 # ranges become invalid when unit changes
84 if self._abcunit and self._abcunit != self._data.get_unit():
85 self._minmaxx = None
86 self._minmaxy = None
87 self._abcunit = self._data.get_unit()
88 self._datamask = None
89 self._plot(self._data)
90 if self._minmaxy is not None:
91 print "setting limits"
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):
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
193 Example:
194 If the data has two IFs/rest frequencies with index 0 and 1
195 for CO and SiO:
196 plotter.set_stacking('i')
197 plotter.set_legend(['CO','SiO'])
198 plotter.plot()
199 plotter.set_legend([r'$^{12}CO$', r'SiO'])
200 """
201 self._lmap = mp
202 if self._data: self.plot(self._data)
203 return
204
205 def set_title(self, title=None):
206 """
207 Set the title of the plot. If multiple panels are plotted,
208 multiple titles have to be specified.
209 Example:
210 # two panels are visible on the plotter
211 plotter.set_title(["First Panel","Second Panel"])
212 """
213 self._title = title
214 if self._data: self.plot(self._data)
215 return
216
217 def set_ordinate(self, ordinate=None):
218 """
219 Set the y-axis label of the plot. If multiple panels are plotted,
220 multiple labels have to be specified.
221 Example:
222 # two panels are visible on the plotter
223 plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
224 """
225 self._ordinate = ordinate
226 if self._data: self.plot(self._data)
227 return
228
229 def set_abcissa(self, abcissa=None):
230 """
231 Set the x-axis label of the plot. If multiple panels are plotted,
232 multiple labels have to be specified.
233 Example:
234 # two panels are visible on the plotter
235 plotter.set_ordinate(["First X-Axis","Second X-Axis"])
236 """
237 self._abcissa = abcissa
238 if self._data: self.plot(self._data)
239 return
240
241 def set_colors(self, colormap):
242 """
243 Set the colors to be used. The plotter will cycle through
244 these colors when lines are overlaid (stacking mode).
245 Example:
246 plotter.set_colors("red green blue")
247 # If for example four lines are overlaid e.g I Q U V
248 # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
249 # and 'V' will be 'red' again.
250 """
251 if isinstance(colormap,str):
252 colormap = colormap.split()
253 self._plotter.palette(0,colormap=colormap)
254 if self._data: self.plot(self._data)
255
256 def set_linestyles(self, linestyles):
257 """
258 Set the linestyles to be used. The plotter will cycle through
259 these linestyles when lines are overlaid (stacking mode) AND
260 only one color has been set.
261 Parameters:
262 linestyles: a list of linestyles to use.
263 'line', 'dashed', 'dotted', 'dashdot',
264 'dashdotdot' and 'dashdashdot' are
265 possible
266
267 Example:
268 plotter.set_colors("black")
269 plotter.set_linestyles("line dashed dotted dashdot")
270 # If for example four lines are overlaid e.g I Q U V
271 # 'I' will be 'solid', 'Q' will be 'dashed',
272 # U will be 'dotted' and 'V' will be 'dashdot'.
273 """
274 if isinstance(linestyles,str):
275 linestyles = linestyles.split()
276 self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
277 if self._data: self.plot(self._data)
278
279 def save(self, filename=None, orientation=None, dpi=None):
280 """
281 Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
282 Parameters:
283 filename: The name of the output file. This is optional
284 and autodetects the image format from the file
285 suffix. If non filename is specified a file
286 called 'yyyymmdd_hhmmss.png' is created in the
287 current directory.
288 orientation: optional parameter for postscript only (not eps).
289 'landscape', 'portrait' or None (default) are valid.
290 If None is choosen for 'ps' output, the plot is
291 automatically oriented to fill the page.
292 dpi: The dpi of the output non-ps plot
293 """
294 self._plotter.save(filename,orientation,dpi)
295 return
296
297
298 def set_mask(self, mask=None, selection=None):
299 """
300 Set a plotting mask for a specific polarization.
301 This is useful for masking out "noise" Pangle outside a source.
302 Parameters:
303 mask: a mask from scantable.create_mask
304 selection: the spectra to apply the mask to.
305 Example:
306 select = selector()
307 select.setpolstrings("Pangle")
308 plotter.set_mask(mymask, select)
309 """
310 if not self._data:
311 msg = "Can only set mask after a first call to plot()"
312 if rcParams['verbose']:
313 print msg
314 return
315 else:
316 raise RuntimeError(msg)
317 if len(mask):
318 if isinstance(mask, list) or isinstance(mask, tuple):
319 self._usermask = array(mask)
320 else:
321 self._usermask = mask
322 if mask is None and selection is None:
323 self._usermask = []
324 self._maskselection = None
325 if isinstance(selection, selector):
326 self._maskselection = {'b': selection.get_beams(),
327 's': selection.get_scans(),
328 'i': selection.get_ifs(),
329 'p': selection.get_pols(),
330 't': [] }
331 else:
332 self._maskselection = None
333 self.plot(self._data)
334
335 def _slice_indeces(self, data):
336 mn = self._minmaxx[0]
337 mx = self._minmaxx[1]
338 asc = data[0] < data[-1]
339 start=0
340 end = len(data)-1
341 inc = 1
342 if not asc:
343 start = len(data)-1
344 end = 0
345 inc = -1
346 # find min index
347 while data[start] < mn:
348 start+= inc
349 # find max index
350 while data[end] > mx:
351 end-=inc
352 end +=1
353 if start > end:
354 return end,start
355 return start,end
356
357 def _reset(self):
358 self._usermask = []
359 self._usermaskspectra = None
360 self.set_selection(None, False)
361
362 def _plot(self, scan):
363 savesel = scan.get_selection()
364 sel = savesel + self._selection
365 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
366 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
367 order = [d0[self._panelling],d0[self._stacking]]
368 sel.set_order(order)
369 scan.set_selection(sel)
370 d = {'b': scan.getbeam, 's': scan.getscan,
371 'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
372
373 polmodes = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
374 n,nstack = self._get_selected_n(scan)
375 maxpanel, maxstack = 9,4
376 if n > maxpanel or nstack > maxstack:
377 from asap import asaplog
378 msg ="Scan to be plotted contains more than %d selections.\n" \
379 "Selecting first %d selections..." % (maxpanel,maxpanel)
380 asaplog.push(msg)
381 print_log()
382 n = min(n,maxpanel)
383 nstack = min(n,maxstack)
384
385 if n > 1:
386 ganged = rcParams['plotter.ganged']
387 if self._rows and self._cols:
388 n = min(n,self._rows*self._cols)
389 self._plotter.set_panels(rows=self._rows,cols=self._cols,
390 nplots=n,ganged=ganged)
391 else:
392 self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
393 else:
394 self._plotter.set_panels()
395 r=0
396 nr = scan.nrow()
397 a0,b0 = -1,-1
398 allxlim = []
399 newpanel=True
400 panelcount,stackcount = 0,0
401 while r < nr:
402 a = d[self._panelling](r)
403 b = d[self._stacking](r)
404# print r
405# print " a: ",a,a0,panelcount,n
406# print " b: ",b,b0,stackcount,nstack
407 if a > a0 and panelcount < n:
408 if n > 1:
409 self._plotter.subplot(panelcount)
410 #pindex += 1
411 self._plotter.palette(0)
412 #title
413 xlab = self._abcissa and self._abcissa[panelcount] \
414 or scan._getabcissalabel()
415 ylab = self._ordinate and self._ordinate[panelcount] \
416 or scan._get_ordinate_label()
417 self._plotter.set_axes('xlabel',xlab)
418 self._plotter.set_axes('ylabel',ylab)
419 lbl = self._get_label(scan, r, self._panelling, self._title)
420 if isinstance(lbl, list) or isinstance(lbl, tuple):
421 if 0 <= panelcount < len(lbl):
422 lbl = lbl[panelcount]
423 else:
424 # get default label
425 lbl = self._get_label(scan, r, self._panelling, None)
426 self._plotter.set_axes('title',lbl)
427 newpanel = True
428 stackcount =0
429 panelcount += 1
430 if (b > b0 or newpanel) and stackcount < nstack:
431 y = []
432 if len(polmodes):
433 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
434 else:
435 y = scan._getspectrum(r)
436 m = scan._getmask(r)
437 if self._maskselection and len(self._usermask) == len(m):
438 if d[self._stacking](r) in self._maskselection[self._stacking]:
439 print "debug"
440 m = logical_and(m, self._usermask)
441 x = scan._getabcissa(r)
442 if self._minmaxx is not None:
443 s,e = self._slice_indeces(x)
444 x = x[s:e]
445 y = y[s:e]
446 m = m[s:e]
447 if len(x) > 1024 and True:#rcParams['plotter.decimate']:
448 fac = len(x)/1024
449 x = x[::fac]
450 m = m[::fac]
451 y = y[::fac]
452 llbl = self._get_label(scan, r, self._stacking, self._lmap)
453 if isinstance(llbl, list) or isinstance(llbl, tuple):
454 if 0 <= stackcount < len(llbl):
455 # use user label
456 llbl = llbl[stackcount]
457 else:
458 # get default label
459 llbl = self._get_label(scan, r, self._stacking, None)
460 self._plotter.set_line(label=llbl)
461 self._plotter.plot(x,y,m)
462 xlim= self._minmaxx or [min(x),max(x)]
463 allxlim += xlim
464 stackcount += 1
465 # last in colour stack -> autoscale x
466 if stackcount == nstack:
467 allxlim.sort()
468 self._plotter.axes.set_xlim([allxlim[0],allxlim[-1]])
469 # clear
470 allxlim =[]
471
472 newpanel = False
473 a0=a
474 b0=b
475 # ignore following rows
476 if (panelcount == n) and (stackcount == nstack):
477 pass#break
478 r+=1 # next row
479 #reset the selector to the scantable's original
480 scan.set_selection(savesel)
481
482 def set_selection(self, selection=None, refresh=True):
483 self._selection = isinstance(selection,selector) and selection or selector()
484 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
485 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
486 order = [d0[self._panelling],d0[self._stacking]]
487 self._selection.set_order(order)
488 if self._data and refresh: self.plot(self._data)
489
490 def _get_selected_n(self, scan):
491 d1 = {'b': scan.nbeam, 's': scan.nscan,
492 'i': scan.nif, 'p': scan.npol, 't': scan.ncycle }
493 d2 = { 'b': len(self._selection.get_beams()),
494 's': len(self._selection.get_scans()),
495 'i': len(self._selection.get_ifs()),
496 'p': len(self._selection.get_pols()),
497 't': len(self._selection.get_cycles()) }
498 n = d2[self._panelling] or d1[self._panelling]()
499 nstack = d2[self._stacking] or d1[self._stacking]()
500 return n,nstack
501
502 def _get_label(self, scan, row, mode, userlabel=None):
503 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
504 if len(pms):
505 poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
506 else:
507 poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
508 d = {'b': "Beam "+str(scan.getbeam(row)),
509 's': scan._getsourcename(row),
510 'i': "IF"+str(scan.getif(row)),
511 'p': poleval,
512 't': scan._gettime(row) }
513 return userlabel or d[mode]
Note: See TracBrowser for help on using the repository browser.