source: trunk/python/asapplotter.py@ 1027

Last change on this file since 1027 was 1023, checked in by mar637, 19 years ago

The previous histogram plot was mutually exclusive with linestyle, so I am using asapplotbase.hist intead.

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