source: trunk/python/asapplotter.py@ 937

Last change on this file since 937 was 935, checked in by mar637, 19 years ago

re-introduced re-plot

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