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
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 = None
40 if rcParams['plotter.histogram']: self._hist = "steps"
41 else: self._hist = "-"
42
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
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
59 def plot(self, scan=None):
60 """
61 Plot a scantable.
62 Parameters:
63 scan: a scantable
64 Note:
65 If a scantable was specified in a previous call
66 to plot, no argument has to be given to 'replot'
67 NO checking is done that the abcissas of the scantable
68 are consistent e.g. all 'channel' or all 'velocity' etc.
69 """
70 if self._plotter.is_dead:
71 self._plotter = self._newplotter()
72 self._plotter.hold()
73 self._plotter.clear()
74 from asap import scantable
75 if not self._data and not scan:
76 print "please provide a scantable to plot"
77 if isinstance(scan, scantable):
78 if self._data is not None:
79 if scan != self._data:
80 self._data = scan
81 # reset
82 self._reset()
83 else:
84 self._data = scan
85 self._reset()
86 # ranges become invalid when unit changes
87 if self._abcunit and self._abcunit != self._data.get_unit():
88 self._minmaxx = None
89 self._minmaxy = None
90 self._abcunit = self._data.get_unit()
91 self._datamask = None
92 self._plot(self._data)
93 if self._minmaxy is not None:
94 self._plotter.set_limits(ylim=self._minmaxy)
95 self._plotter.release()
96 print_log()
97 return
98
99 def set_mode(self, stacking=None, panelling=None):
100 """
101 Set the plots look and feel, i.e. what you want to see on the plot.
102 Parameters:
103 stacking: tell the plotter which variable to plot
104 as line color overlays (default 'pol')
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 """
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)
123 if self._data: self.plot(self._data)
124 return
125
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)
131 if md:
132 self._panelling = md
133 self._title = None
134 return True
135 return False
136
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
150 if self._data: self.plot(self._data)
151 return
152
153 def set_stacking(self, what=None):
154 mode = what
155 if mode is None:
156 mode = rcParams['plotter.stacking']
157 md = self._translate(mode)
158 if md:
159 self._stacking = md
160 self._lmap = None
161 return True
162 return False
163
164 def set_range(self,xstart=None,xend=None,ystart=None,yend=None):
165 """
166 Set the range of interest on the abcissa of the plot
167 Parameters:
168 [x,y]start,[x,y]end: The start and end points of the 'zoom' window
169 Note:
170 These become non-sensical when the unit changes.
171 use plotter.set_range() without parameters to reset
172
173 """
174 if xstart is None and xend is None:
175 self._minmaxx = None
176 else:
177 self._minmaxx = [xstart,xend]
178 if ystart is None and yend is None:
179 self._minmaxy = None
180 else:
181 self._minmaxy = [ystart,yend]
182 if self._data: self.plot(self._data)
183 return
184
185 def set_legend(self, mp=None):
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
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}$'
194
195 Example:
196 If the data has two IFs/rest frequencies with index 0 and 1
197 for CO and SiO:
198 plotter.set_stacking('i')
199 plotter.set_legend(['CO','SiO'])
200 plotter.plot()
201 plotter.set_legend([r'$^{12}CO$', r'SiO'])
202 """
203 self._lmap = mp
204 if self._data: self.plot(self._data)
205 return
206
207 def set_title(self, title=None):
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 """
215 self._title = title
216 if self._data: self.plot(self._data)
217 return
218
219 def set_ordinate(self, ordinate=None):
220 """
221 Set the y-axis label of the plot. If multiple panels are plotted,
222 multiple labels have to be specified.
223 Parameters:
224 ordinate: a list of ordinate labels. None (default) let
225 data determine the labels
226 Example:
227 # two panels are visible on the plotter
228 plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
229 """
230 self._ordinate = ordinate
231 if self._data: self.plot(self._data)
232 return
233
234 def set_abcissa(self, abcissa=None):
235 """
236 Set the x-axis label of the plot. If multiple panels are plotted,
237 multiple labels have to be specified.
238 Parameters:
239 abcissa: a list of abcissa labels. None (default) let
240 data determine the labels
241 Example:
242 # two panels are visible on the plotter
243 plotter.set_ordinate(["First X-Axis","Second X-Axis"])
244 """
245 self._abcissa = abcissa
246 if self._data: self.plot(self._data)
247 return
248
249 def set_colors(self, colormap):
250 """
251 Set the colors to be used. The plotter will cycle through
252 these colors when lines are overlaid (stacking mode).
253 Parameters:
254 colormap: a list of colour names
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)
264 if self._data: self.plot(self._data)
265
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
278 def set_linestyles(self, linestyles):
279 """
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.
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)
299 if self._data: self.plot(self._data)
300
301 def save(self, filename=None, orientation=None, dpi=None):
302 """
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.
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.
314 dpi: The dpi of the output non-ps plot
315 """
316 self._plotter.save(filename,orientation,dpi)
317 return
318
319
320 def set_mask(self, mask=None, selection=None):
321 """
322 Set a plotting mask for a specific polarization.
323 This is useful for masking out "noise" Pangle outside a source.
324 Parameters:
325 mask: a mask from scantable.create_mask
326 selection: the spectra to apply the mask to.
327 Example:
328 select = selector()
329 select.setpolstrings("Pangle")
330 plotter.set_mask(mymask, select)
331 """
332 if not self._data:
333 msg = "Can only set mask after a first call to plot()"
334 if rcParams['verbose']:
335 print msg
336 return
337 else:
338 raise RuntimeError(msg)
339 if len(mask):
340 if isinstance(mask, list) or isinstance(mask, tuple):
341 self._usermask = array(mask)
342 else:
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):
348 self._maskselection = {'b': selection.get_beams(),
349 's': selection.get_scans(),
350 'i': selection.get_ifs(),
351 'p': selection.get_pols(),
352 't': [] }
353 else:
354 self._maskselection = None
355 self.plot(self._data)
356
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
379 def _reset(self):
380 self._usermask = []
381 self._usermaskspectra = None
382 self.set_selection(None, False)
383
384 def _plot(self, scan):
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)
392 d = {'b': scan.getbeam, 's': scan.getscan,
393 'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
394
395 polmodes = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
396 n,nstack = self._get_selected_n(scan)
397 maxpanel, maxstack = 16,8
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)
405 nstack = min(nstack,maxstack)
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 = []
421 allylim = []
422 newpanel=True
423 panelcount,stackcount = 0,0
424 while r < nr:
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]
466 if len(x) > 2048 and rcParams['plotter.decimate']:
467 fac = len(x)/2048
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)
480 self._plotter.set_line(linestyle=self._hist)
481 self._plotter.plot(x,y,m)
482 xlim= self._minmaxx or [min(x),max(x)]
483 allxlim += xlim
484 ylim= self._minmaxy or [min(y),max(y)]
485 allylim += ylim
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):
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]])
503 break
504 r+=1 # next row
505 #reset the selector to the scantable's original
506 scan.set_selection(savesel)
507
508 def set_selection(self, selection=None, refresh=True):
509 self._selection = isinstance(selection,selector) and selection or selector()
510 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
511 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
512 order = [d0[self._panelling],d0[self._stacking]]
513 self._selection.set_order(order)
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 }
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()) }
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):
529 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
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)),
537 'p': poleval,
538 't': scan._gettime(row) }
539 return userlabel or d[mode]
Note: See TracBrowser for help on using the repository browser.