source: trunk/python/asapplotter.py@ 1097

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

added legend placing/disable to set_legend. Changed decimate from 2048 to 1024

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