source: trunk/python/asapplotter.py@ 934

Last change on this file since 934 was 920, checked in by mar637, 19 years ago

asap2 migration of plotter

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