source: trunk/python/asapplotter.py@ 1020

Last change on this file since 1020 was 1018, checked in by mar637, 19 years ago

added yrange synch in 'ganged' mode.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.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
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 self._plotter.set_limits(ylim=self._minmaxy)
92 self._plotter.release()
93 print_log()
94 return
95
96 def set_mode(self, stacking=None, panelling=None):
97 """
98 Set the plots look and feel, i.e. what you want to see on the plot.
99 Parameters:
100 stacking: tell the plotter which variable to plot
101 as line color overlays (default 'pol')
102 panelling: tell the plotter which variable to plot
103 across multiple panels (default 'scan'
104 Note:
105 Valid modes are:
106 'beam' 'Beam' 'b': Beams
107 'if' 'IF' 'i': IFs
108 'pol' 'Pol' 'p': Polarisations
109 'scan' 'Scan' 's': Scans
110 'time' 'Time' 't': Times
111 """
112 msg = "Invalid mode"
113 if not self.set_panelling(panelling) or \
114 not self.set_stacking(stacking):
115 if rcParams['verbose']:
116 print msg
117 return
118 else:
119 raise TypeError(msg)
120 if self._data: self.plot(self._data)
121 return
122
123 def set_panelling(self, what=None):
124 mode = what
125 if mode is None:
126 mode = rcParams['plotter.panelling']
127 md = self._translate(mode)
128 if md:
129 self._panelling = md
130 self._title = None
131 return True
132 return False
133
134 def set_layout(self,rows=None,cols=None):
135 """
136 Set the multi-panel layout, i.e. how many rows and columns plots
137 are visible.
138 Parameters:
139 rows: The number of rows of plots
140 cols: The number of columns of plots
141 Note:
142 If no argument is given, the potter reverts to its auto-plot
143 behaviour.
144 """
145 self._rows = rows
146 self._cols = cols
147 if self._data: self.plot(self._data)
148 return
149
150 def set_stacking(self, what=None):
151 mode = what
152 if mode is None:
153 mode = rcParams['plotter.stacking']
154 md = self._translate(mode)
155 if md:
156 self._stacking = md
157 self._lmap = None
158 return True
159 return False
160
161 def set_range(self,xstart=None,xend=None,ystart=None,yend=None):
162 """
163 Set the range of interest on the abcissa of the plot
164 Parameters:
165 [x,y]start,[x,y]end: The start and end points of the 'zoom' window
166 Note:
167 These become non-sensical when the unit changes.
168 use plotter.set_range() without parameters to reset
169
170 """
171 if xstart is None and xend is None:
172 self._minmaxx = None
173 else:
174 self._minmaxx = [xstart,xend]
175 if ystart is None and yend is None:
176 self._minmaxy = None
177 else:
178 self._minmaxy = [ystart,yend]
179 if self._data: self.plot(self._data)
180 return
181
182 def set_legend(self, mp=None):
183 """
184 Specify a mapping for the legend instead of using the default
185 indices:
186 Parameters:
187 mp: a list of 'strings'. This should have the same length
188 as the number of elements on the legend and then maps
189 to the indeces in order. It is possible to uses latex
190 math expression. These have to be enclosed in r'', e.g. r'$x^{2}$'
191
192 Example:
193 If the data has two IFs/rest frequencies with index 0 and 1
194 for CO and SiO:
195 plotter.set_stacking('i')
196 plotter.set_legend(['CO','SiO'])
197 plotter.plot()
198 plotter.set_legend([r'$^{12}CO$', r'SiO'])
199 """
200 self._lmap = mp
201 if self._data: self.plot(self._data)
202 return
203
204 def set_title(self, title=None):
205 """
206 Set the title of the plot. If multiple panels are plotted,
207 multiple titles have to be specified.
208 Example:
209 # two panels are visible on the plotter
210 plotter.set_title(["First Panel","Second Panel"])
211 """
212 self._title = title
213 if self._data: self.plot(self._data)
214 return
215
216 def set_ordinate(self, ordinate=None):
217 """
218 Set the y-axis label of the plot. If multiple panels are plotted,
219 multiple labels have to be specified.
220 Example:
221 # two panels are visible on the plotter
222 plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
223 """
224 self._ordinate = ordinate
225 if self._data: self.plot(self._data)
226 return
227
228 def set_abcissa(self, abcissa=None):
229 """
230 Set the x-axis label of the plot. If multiple panels are plotted,
231 multiple labels have to be specified.
232 Example:
233 # two panels are visible on the plotter
234 plotter.set_ordinate(["First X-Axis","Second X-Axis"])
235 """
236 self._abcissa = abcissa
237 if self._data: self.plot(self._data)
238 return
239
240 def set_colors(self, colormap):
241 """
242 Set the colors to be used. The plotter will cycle through
243 these colors when lines are overlaid (stacking mode).
244 Example:
245 plotter.set_colors("red green blue")
246 # If for example four lines are overlaid e.g I Q U V
247 # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
248 # and 'V' will be 'red' again.
249 """
250 if isinstance(colormap,str):
251 colormap = colormap.split()
252 self._plotter.palette(0,colormap=colormap)
253 if self._data: self.plot(self._data)
254
255 def set_linestyles(self, linestyles):
256 """
257 Set the linestyles to be used. The plotter will cycle through
258 these linestyles when lines are overlaid (stacking mode) AND
259 only one color has been set.
260 Parameters:
261 linestyles: a list of linestyles to use.
262 'line', 'dashed', 'dotted', 'dashdot',
263 'dashdotdot' and 'dashdashdot' are
264 possible
265
266 Example:
267 plotter.set_colors("black")
268 plotter.set_linestyles("line dashed dotted dashdot")
269 # If for example four lines are overlaid e.g I Q U V
270 # 'I' will be 'solid', 'Q' will be 'dashed',
271 # U will be 'dotted' and 'V' will be 'dashdot'.
272 """
273 if isinstance(linestyles,str):
274 linestyles = linestyles.split()
275 self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
276 if self._data: self.plot(self._data)
277
278 def save(self, filename=None, orientation=None, dpi=None):
279 """
280 Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
281 Parameters:
282 filename: The name of the output file. This is optional
283 and autodetects the image format from the file
284 suffix. If non filename is specified a file
285 called 'yyyymmdd_hhmmss.png' is created in the
286 current directory.
287 orientation: optional parameter for postscript only (not eps).
288 'landscape', 'portrait' or None (default) are valid.
289 If None is choosen for 'ps' output, the plot is
290 automatically oriented to fill the page.
291 dpi: The dpi of the output non-ps plot
292 """
293 self._plotter.save(filename,orientation,dpi)
294 return
295
296
297 def set_mask(self, mask=None, selection=None):
298 """
299 Set a plotting mask for a specific polarization.
300 This is useful for masking out "noise" Pangle outside a source.
301 Parameters:
302 mask: a mask from scantable.create_mask
303 selection: the spectra to apply the mask to.
304 Example:
305 select = selector()
306 select.setpolstrings("Pangle")
307 plotter.set_mask(mymask, select)
308 """
309 if not self._data:
310 msg = "Can only set mask after a first call to plot()"
311 if rcParams['verbose']:
312 print msg
313 return
314 else:
315 raise RuntimeError(msg)
316 if len(mask):
317 if isinstance(mask, list) or isinstance(mask, tuple):
318 self._usermask = array(mask)
319 else:
320 self._usermask = mask
321 if mask is None and selection is None:
322 self._usermask = []
323 self._maskselection = None
324 if isinstance(selection, selector):
325 self._maskselection = {'b': selection.get_beams(),
326 's': selection.get_scans(),
327 'i': selection.get_ifs(),
328 'p': selection.get_pols(),
329 't': [] }
330 else:
331 self._maskselection = None
332 self.plot(self._data)
333
334 def _slice_indeces(self, data):
335 mn = self._minmaxx[0]
336 mx = self._minmaxx[1]
337 asc = data[0] < data[-1]
338 start=0
339 end = len(data)-1
340 inc = 1
341 if not asc:
342 start = len(data)-1
343 end = 0
344 inc = -1
345 # find min index
346 while data[start] < mn:
347 start+= inc
348 # find max index
349 while data[end] > mx:
350 end-=inc
351 end +=1
352 if start > end:
353 return end,start
354 return start,end
355
356 def _reset(self):
357 self._usermask = []
358 self._usermaskspectra = None
359 self.set_selection(None, False)
360
361 def _plot(self, scan):
362 savesel = scan.get_selection()
363 sel = savesel + self._selection
364 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
365 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
366 order = [d0[self._panelling],d0[self._stacking]]
367 sel.set_order(order)
368 scan.set_selection(sel)
369 d = {'b': scan.getbeam, 's': scan.getscan,
370 'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
371
372 polmodes = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
373 n,nstack = self._get_selected_n(scan)
374 maxpanel, maxstack = 16,8
375 if n > maxpanel or nstack > maxstack:
376 from asap import asaplog
377 msg ="Scan to be plotted contains more than %d selections.\n" \
378 "Selecting first %d selections..." % (maxpanel,maxpanel)
379 asaplog.push(msg)
380 print_log()
381 n = min(n,maxpanel)
382 nstack = min(nstack,maxstack)
383
384 if n > 1:
385 ganged = rcParams['plotter.ganged']
386 if self._rows and self._cols:
387 n = min(n,self._rows*self._cols)
388 self._plotter.set_panels(rows=self._rows,cols=self._cols,
389 nplots=n,ganged=ganged)
390 else:
391 self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
392 else:
393 self._plotter.set_panels()
394 r=0
395 nr = scan.nrow()
396 a0,b0 = -1,-1
397 allxlim = []
398 allylim = []
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 if a > a0 and panelcount < n:
405 if n > 1:
406 self._plotter.subplot(panelcount)
407 self._plotter.palette(0)
408 #title
409 xlab = self._abcissa and self._abcissa[panelcount] \
410 or scan._getabcissalabel()
411 ylab = self._ordinate and self._ordinate[panelcount] \
412 or scan._get_ordinate_label()
413 self._plotter.set_axes('xlabel',xlab)
414 self._plotter.set_axes('ylabel',ylab)
415 lbl = self._get_label(scan, r, self._panelling, self._title)
416 if isinstance(lbl, list) or isinstance(lbl, tuple):
417 if 0 <= panelcount < len(lbl):
418 lbl = lbl[panelcount]
419 else:
420 # get default label
421 lbl = self._get_label(scan, r, self._panelling, None)
422 self._plotter.set_axes('title',lbl)
423 newpanel = True
424 stackcount =0
425 panelcount += 1
426 if (b > b0 or newpanel) and stackcount < nstack:
427 y = []
428 if len(polmodes):
429 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
430 else:
431 y = scan._getspectrum(r)
432 m = scan._getmask(r)
433 if self._maskselection and len(self._usermask) == len(m):
434 if d[self._stacking](r) in self._maskselection[self._stacking]:
435 print "debug"
436 m = logical_and(m, self._usermask)
437 x = scan._getabcissa(r)
438 if self._minmaxx is not None:
439 s,e = self._slice_indeces(x)
440 x = x[s:e]
441 y = y[s:e]
442 m = m[s:e]
443 if len(x) > 1024 and True:#rcParams['plotter.decimate']:
444 fac = len(x)/1024
445 x = x[::fac]
446 m = m[::fac]
447 y = y[::fac]
448 llbl = self._get_label(scan, r, self._stacking, self._lmap)
449 if isinstance(llbl, list) or isinstance(llbl, tuple):
450 if 0 <= stackcount < len(llbl):
451 # use user label
452 llbl = llbl[stackcount]
453 else:
454 # get default label
455 llbl = self._get_label(scan, r, self._stacking, None)
456 self._plotter.set_line(label=llbl)
457 self._plotter.plot(x,y,m)
458 xlim= self._minmaxx or [min(x),max(x)]
459 allxlim += xlim
460 ylim= self._minmaxy or [min(y),max(y)]
461 allylim += ylim
462 stackcount += 1
463 # last in colour stack -> autoscale x
464 if stackcount == nstack:
465 allxlim.sort()
466 self._plotter.axes.set_xlim([allxlim[0],allxlim[-1]])
467 # clear
468 allxlim =[]
469
470 newpanel = False
471 a0=a
472 b0=b
473 # ignore following rows
474 if (panelcount == n) and (stackcount == nstack):
475 # last panel -> autoscale y if ganged
476 if rcParams['plotter.ganged']:
477 allylim.sort()
478 self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
479 break
480 r+=1 # next row
481 #reset the selector to the scantable's original
482 scan.set_selection(savesel)
483
484 def set_selection(self, selection=None, refresh=True):
485 self._selection = isinstance(selection,selector) and selection or selector()
486 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
487 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
488 order = [d0[self._panelling],d0[self._stacking]]
489 self._selection.set_order(order)
490 if self._data and refresh: self.plot(self._data)
491
492 def _get_selected_n(self, scan):
493 d1 = {'b': scan.nbeam, 's': scan.nscan,
494 'i': scan.nif, 'p': scan.npol, 't': scan.ncycle }
495 d2 = { 'b': len(self._selection.get_beams()),
496 's': len(self._selection.get_scans()),
497 'i': len(self._selection.get_ifs()),
498 'p': len(self._selection.get_pols()),
499 't': len(self._selection.get_cycles()) }
500 n = d2[self._panelling] or d1[self._panelling]()
501 nstack = d2[self._stacking] or d1[self._stacking]()
502 return n,nstack
503
504 def _get_label(self, scan, row, mode, userlabel=None):
505 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
506 if len(pms):
507 poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
508 else:
509 poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
510 d = {'b': "Beam "+str(scan.getbeam(row)),
511 's': scan._getsourcename(row),
512 'i': "IF"+str(scan.getif(row)),
513 'p': poleval,
514 't': scan._gettime(row) }
515 return userlabel or d[mode]
Note: See TracBrowser for help on using the repository browser.