source: trunk/python/asapplotter.py@ 958

Last change on this file since 958 was 947, checked in by mar637, 19 years ago

made use of selector class, added sort order to _plot command

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
RevLine 
[947]1from asap import rcParams, print_log, selector
[709]2from numarray import logical_and
[203]3
4class asapplotter:
[226]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 """
[734]13 def __init__(self, visible=None):
14 self._visible = rcParams['plotter.gui']
15 if visible is not None:
16 self._visible = visible
[710]17 self._plotter = self._newplotter()
18
[920]19
[554]20 self._panelling = None
21 self._stacking = None
22 self.set_panelling()
23 self.set_stacking()
[377]24 self._rows = None
25 self._cols = None
[203]26 self._autoplot = False
[525]27 self._minmaxx = None
28 self._minmaxy = None
[710]29 self._datamask = None
[203]30 self._data = None
[607]31 self._lmap = None
[226]32 self._title = None
[257]33 self._ordinate = None
34 self._abcissa = None
[709]35 self._abcunit = None
[920]36 self._usermask = []
37 self._maskselection = None
38 self._selection = selector()
[203]39
[920]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
[710]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
[935]56 def plot(self, scan=None):
[203]57 """
[920]58 Plot a scantable.
[203]59 Parameters:
[920]60 scan: a scantable
[203]61 Note:
[920]62 If a scantable was specified in a previous call
[203]63 to plot, no argument has to be given to 'replot'
[920]64 NO checking is done that the abcissas of the scantable
[203]65 are consistent e.g. all 'channel' or all 'velocity' etc.
66 """
[710]67 if self._plotter.is_dead:
68 self._plotter = self._newplotter()
[600]69 self._plotter.hold()
[203]70 self._plotter.clear()
[920]71 from asap import scantable
[935]72 if not self._data and not scan:
73 print "please provide a scantable to plot"
[920]74 if isinstance(scan, scantable):
[709]75 if self._data is not None:
[920]76 if scan != self._data:
77 self._data = scan
[710]78 # reset
79 self._reset()
[525]80 else:
[920]81 self._data = scan
[710]82 self._reset()
[709]83 # ranges become invalid when unit changes
[935]84 if self._abcunit and self._abcunit != self._data.get_unit():
[709]85 self._minmaxx = None
86 self._minmaxy = None
[920]87 self._abcunit = self._data.get_unit()
[710]88 self._datamask = None
[920]89 self._plot(self._data)
[709]90 if self._minmaxy is not None:
[935]91 print "setting limits"
[709]92 self._plotter.set_limits(ylim=self._minmaxy)
[203]93 self._plotter.release()
[753]94 print_log()
[203]95 return
96
[226]97 def set_mode(self, stacking=None, panelling=None):
[203]98 """
[377]99 Set the plots look and feel, i.e. what you want to see on the plot.
[203]100 Parameters:
101 stacking: tell the plotter which variable to plot
[710]102 as line color overlays (default 'pol')
[203]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 """
[753]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)
[920]121 if self._data: self.plot(self._data)
[203]122 return
123
[554]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)
[203]129 if md:
[554]130 self._panelling = md
[226]131 self._title = None
[203]132 return True
133 return False
134
[377]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
[920]148 if self._data: self.plot(self._data)
[377]149 return
150
[709]151 def set_stacking(self, what=None):
[554]152 mode = what
[709]153 if mode is None:
154 mode = rcParams['plotter.stacking']
[554]155 md = self._translate(mode)
[203]156 if md:
157 self._stacking = md
[226]158 self._lmap = None
[203]159 return True
160 return False
161
[525]162 def set_range(self,xstart=None,xend=None,ystart=None,yend=None):
[203]163 """
164 Set the range of interest on the abcissa of the plot
165 Parameters:
[525]166 [x,y]start,[x,y]end: The start and end points of the 'zoom' window
[203]167 Note:
168 These become non-sensical when the unit changes.
169 use plotter.set_range() without parameters to reset
170
171 """
[525]172 if xstart is None and xend is None:
173 self._minmaxx = None
[600]174 else:
175 self._minmaxx = [xstart,xend]
[525]176 if ystart is None and yend is None:
177 self._minmaxy = None
[600]178 else:
[709]179 self._minmaxy = [ystart,yend]
[920]180 if self._data: self.plot(self._data)
[203]181 return
[709]182
[257]183 def set_legend(self, mp=None):
[203]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
[710]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}$'
[203]192
193 Example:
[485]194 If the data has two IFs/rest frequencies with index 0 and 1
[203]195 for CO and SiO:
196 plotter.set_stacking('i')
[710]197 plotter.set_legend(['CO','SiO'])
[203]198 plotter.plot()
[710]199 plotter.set_legend([r'$^{12}CO$', r'SiO'])
[203]200 """
201 self._lmap = mp
[920]202 if self._data: self.plot(self._data)
[226]203 return
204
205 def set_title(self, title=None):
[710]206 """
207 Set the title of the plot. If multiple panels are plotted,
208 multiple titles have to be specified.
209 Example:
210 # two panels are visible on the plotter
211 plotter.set_title(["First Panel","Second Panel"])
212 """
[226]213 self._title = title
[920]214 if self._data: self.plot(self._data)
[226]215 return
216
[257]217 def set_ordinate(self, ordinate=None):
[710]218 """
219 Set the y-axis label of the plot. If multiple panels are plotted,
220 multiple labels have to be specified.
221 Example:
222 # two panels are visible on the plotter
223 plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
224 """
[257]225 self._ordinate = ordinate
[920]226 if self._data: self.plot(self._data)
[257]227 return
228
229 def set_abcissa(self, abcissa=None):
[710]230 """
231 Set the x-axis label of the plot. If multiple panels are plotted,
232 multiple labels have to be specified.
233 Example:
234 # two panels are visible on the plotter
235 plotter.set_ordinate(["First X-Axis","Second X-Axis"])
236 """
[257]237 self._abcissa = abcissa
[920]238 if self._data: self.plot(self._data)
[257]239 return
240
[710]241 def set_colors(self, colormap):
[377]242 """
[710]243 Set the colors to be used. The plotter will cycle through
244 these colors when lines are overlaid (stacking mode).
245 Example:
246 plotter.set_colors("red green blue")
247 # If for example four lines are overlaid e.g I Q U V
248 # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
249 # and 'V' will be 'red' again.
250 """
251 if isinstance(colormap,str):
252 colormap = colormap.split()
253 self._plotter.palette(0,colormap=colormap)
[920]254 if self._data: self.plot(self._data)
[710]255
256 def set_linestyles(self, linestyles):
257 """
[734]258 Set the linestyles to be used. The plotter will cycle through
259 these linestyles when lines are overlaid (stacking mode) AND
260 only one color has been set.
[710]261 Parameters:
262 linestyles: a list of linestyles to use.
263 'line', 'dashed', 'dotted', 'dashdot',
264 'dashdotdot' and 'dashdashdot' are
265 possible
266
267 Example:
268 plotter.set_colors("black")
269 plotter.set_linestyles("line dashed dotted dashdot")
270 # If for example four lines are overlaid e.g I Q U V
271 # 'I' will be 'solid', 'Q' will be 'dashed',
272 # U will be 'dotted' and 'V' will be 'dashdot'.
273 """
274 if isinstance(linestyles,str):
275 linestyles = linestyles.split()
276 self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
[920]277 if self._data: self.plot(self._data)
[710]278
279 def save(self, filename=None, orientation=None, dpi=None):
280 """
[377]281 Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
282 Parameters:
283 filename: The name of the output file. This is optional
284 and autodetects the image format from the file
285 suffix. If non filename is specified a file
286 called 'yyyymmdd_hhmmss.png' is created in the
287 current directory.
[709]288 orientation: optional parameter for postscript only (not eps).
289 'landscape', 'portrait' or None (default) are valid.
290 If None is choosen for 'ps' output, the plot is
291 automatically oriented to fill the page.
[710]292 dpi: The dpi of the output non-ps plot
[377]293 """
[709]294 self._plotter.save(filename,orientation,dpi)
[377]295 return
[709]296
[257]297
[920]298 def set_mask(self, mask=None, selection=None):
[525]299 """
[734]300 Set a plotting mask for a specific polarization.
301 This is useful for masking out "noise" Pangle outside a source.
302 Parameters:
[920]303 mask: a mask from scantable.create_mask
304 selection: the spectra to apply the mask to.
[734]305 Example:
[920]306 select = selector()
307 select.setpolstrings("Pangle")
308 plotter.set_mask(mymask, select)
[734]309 """
[710]310 if not self._data:
[920]311 msg = "Can only set mask after a first call to plot()"
[753]312 if rcParams['verbose']:
313 print msg
[762]314 return
[753]315 else:
[762]316 raise RuntimeError(msg)
[920]317 if len(mask):
318 if isinstance(mask, list) or isinstance(mask, tuple):
319 self._usermask = array(mask)
[710]320 else:
[920]321 self._usermask = mask
322 if mask is None and selection is None:
323 self._usermask = []
324 self._maskselection = None
325 if isinstance(selection, selector):
[947]326 self._maskselection = {'b': selection.get_beams(),
327 's': selection.get_scans(),
328 'i': selection.get_ifs(),
329 'p': selection.get_pols(),
[920]330 't': [] }
[710]331 else:
[920]332 self._maskselection = None
333 self.plot(self._data)
[710]334
[709]335 def _slice_indeces(self, data):
336 mn = self._minmaxx[0]
337 mx = self._minmaxx[1]
338 asc = data[0] < data[-1]
339 start=0
340 end = len(data)-1
341 inc = 1
342 if not asc:
343 start = len(data)-1
344 end = 0
345 inc = -1
346 # find min index
347 while data[start] < mn:
348 start+= inc
349 # find max index
350 while data[end] > mx:
351 end-=inc
352 end +=1
353 if start > end:
354 return end,start
355 return start,end
356
[710]357 def _reset(self):
[920]358 self._usermask = []
[710]359 self._usermaskspectra = None
[920]360 self.set_selection(None, False)
361
362 def _plot(self, scan):
[947]363 savesel = scan.get_selection()
364 sel = savesel + self._selection
365 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
366 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
367 order = [d0[self._panelling],d0[self._stacking]]
368 sel.set_order(order)
369 scan.set_selection(sel)
[920]370 d = {'b': scan.getbeam, 's': scan.getscan,
371 'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
372
[947]373 polmodes = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
[920]374 n,nstack = self._get_selected_n(scan)
375 maxpanel, maxstack = 9,4
376 if n > maxpanel or nstack > maxstack:
377 from asap import asaplog
378 msg ="Scan to be plotted contains more than %d selections.\n" \
379 "Selecting first %d selections..." % (maxpanel,maxpanel)
380 asaplog.push(msg)
381 print_log()
382 n = min(n,maxpanel)
383 nstack = min(n,maxstack)
384
385 if n > 1:
386 ganged = rcParams['plotter.ganged']
387 if self._rows and self._cols:
388 n = min(n,self._rows*self._cols)
389 self._plotter.set_panels(rows=self._rows,cols=self._cols,
390 nplots=n,ganged=ganged)
391 else:
392 self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
393 else:
394 self._plotter.set_panels()
395 r=0
396 nr = scan.nrow()
397 a0,b0 = -1,-1
398 allxlim = []
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# print r
405# print " a: ",a,a0,panelcount,n
406# print " b: ",b,b0,stackcount,nstack
407 if a > a0 and panelcount < n:
408 if n > 1:
409 self._plotter.subplot(panelcount)
410 #pindex += 1
411 self._plotter.palette(0)
412 #title
413 xlab = self._abcissa and self._abcissa[panelcount] \
414 or scan._getabcissalabel()
415 ylab = self._ordinate and self._ordinate[panelcount] \
416 or scan._get_ordinate_label()
417 self._plotter.set_axes('xlabel',xlab)
418 self._plotter.set_axes('ylabel',ylab)
419 lbl = self._get_label(scan, r, self._panelling, self._title)
420 if isinstance(lbl, list) or isinstance(lbl, tuple):
421 if 0 <= panelcount < len(lbl):
422 lbl = lbl[panelcount]
423 else:
424 # get default label
425 lbl = self._get_label(scan, r, self._panelling, None)
426 self._plotter.set_axes('title',lbl)
427 newpanel = True
428 stackcount =0
429 panelcount += 1
430 if (b > b0 or newpanel) and stackcount < nstack:
431 y = []
432 if len(polmodes):
433 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
434 else:
435 y = scan._getspectrum(r)
436 m = scan._getmask(r)
437 if self._maskselection and len(self._usermask) == len(m):
438 if d[self._stacking](r) in self._maskselection[self._stacking]:
439 print "debug"
440 m = logical_and(m, self._usermask)
441 x = scan._getabcissa(r)
442 if self._minmaxx is not None:
443 s,e = self._slice_indeces(x)
444 x = x[s:e]
445 y = y[s:e]
446 m = m[s:e]
447 if len(x) > 1024 and True:#rcParams['plotter.decimate']:
448 fac = len(x)/1024
449 x = x[::fac]
450 m = m[::fac]
451 y = y[::fac]
452 llbl = self._get_label(scan, r, self._stacking, self._lmap)
453 if isinstance(llbl, list) or isinstance(llbl, tuple):
454 if 0 <= stackcount < len(llbl):
455 # use user label
456 llbl = llbl[stackcount]
457 else:
458 # get default label
459 llbl = self._get_label(scan, r, self._stacking, None)
460 self._plotter.set_line(label=llbl)
461 self._plotter.plot(x,y,m)
462 xlim= self._minmaxx or [min(x),max(x)]
463 allxlim += xlim
464 stackcount += 1
465 # last in colour stack -> autoscale x
466 if stackcount == nstack:
467 allxlim.sort()
468 self._plotter.axes.set_xlim([allxlim[0],allxlim[-1]])
469 # clear
470 allxlim =[]
471
472 newpanel = False
473 a0=a
474 b0=b
475 # ignore following rows
476 if (panelcount == n) and (stackcount == nstack):
477 pass#break
478 r+=1 # next row
[947]479 #reset the selector to the scantable's original
480 scan.set_selection(savesel)
[920]481
482 def set_selection(self, selection=None, refresh=True):
[947]483 self._selection = isinstance(selection,selector) and selection or selector()
[920]484 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
485 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
486 order = [d0[self._panelling],d0[self._stacking]]
[947]487 self._selection.set_order(order)
[920]488 if self._data and refresh: self.plot(self._data)
489
490 def _get_selected_n(self, scan):
491 d1 = {'b': scan.nbeam, 's': scan.nscan,
492 'i': scan.nif, 'p': scan.npol, 't': scan.ncycle }
[947]493 d2 = { 'b': len(self._selection.get_beams()),
494 's': len(self._selection.get_scans()),
495 'i': len(self._selection.get_ifs()),
496 'p': len(self._selection.get_pols()),
497 't': len(self._selection.get_cycles()) }
[920]498 n = d2[self._panelling] or d1[self._panelling]()
499 nstack = d2[self._stacking] or d1[self._stacking]()
500 return n,nstack
501
502 def _get_label(self, scan, row, mode, userlabel=None):
[947]503 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
[920]504 if len(pms):
505 poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
506 else:
507 poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
508 d = {'b': "Beam "+str(scan.getbeam(row)),
509 's': scan._getsourcename(row),
510 'i': "IF"+str(scan.getif(row)),
511 'p': scan.poltype().capitalize()+" "+poleval,
512 't': scan._gettime(row) }
513 return userlabel or d[mode]
Note: See TracBrowser for help on using the repository browser.