source: trunk/python/asapplotter.py@ 1100

Last change on this file since 1100 was 1096, checked in by mar637, 18 years ago

added legend placing/disable to set_legend. Changed decimate from 2048 to 1024

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.4 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
[1096]183 def set_legend(self, mp=None, mode = 0):
[203]184 """
185 Specify a mapping for the legend instead of using the default
186 indices:
187 Parameters:
[1096]188 mp: a list of 'strings'. This should have the same length
[203]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}$'
[1096]192 mode: where to display the legend
193 Any other value for loc else disables the legend:
194 0: auto
195 1: upper right
196 2: upper left
197 3: lower left
198 4: lower right
199 5: right
200 6: center left
201 7: center right
202 8: lower center
203 9: upper center
204 10: center
[203]205
206 Example:
[485]207 If the data has two IFs/rest frequencies with index 0 and 1
[203]208 for CO and SiO:
209 plotter.set_stacking('i')
[710]210 plotter.set_legend(['CO','SiO'])
[203]211 plotter.plot()
[710]212 plotter.set_legend([r'$^{12}CO$', r'SiO'])
[203]213 """
214 self._lmap = mp
[1096]215 self._plotter.legend(mode)
216 if self._data:
217 self.plot(self._data)
[226]218 return
219
220 def set_title(self, title=None):
[710]221 """
222 Set the title of the plot. If multiple panels are plotted,
223 multiple titles have to be specified.
224 Example:
225 # two panels are visible on the plotter
226 plotter.set_title(["First Panel","Second Panel"])
227 """
[226]228 self._title = title
[920]229 if self._data: self.plot(self._data)
[226]230 return
231
[257]232 def set_ordinate(self, ordinate=None):
[710]233 """
234 Set the y-axis label of the plot. If multiple panels are plotted,
235 multiple labels have to be specified.
[1021]236 Parameters:
237 ordinate: a list of ordinate 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 Y-Axis","Second Y-Axis"])
242 """
[257]243 self._ordinate = ordinate
[920]244 if self._data: self.plot(self._data)
[257]245 return
246
247 def set_abcissa(self, abcissa=None):
[710]248 """
249 Set the x-axis label of the plot. If multiple panels are plotted,
250 multiple labels have to be specified.
[1021]251 Parameters:
252 abcissa: a list of abcissa labels. None (default) let
253 data determine the labels
[710]254 Example:
255 # two panels are visible on the plotter
256 plotter.set_ordinate(["First X-Axis","Second X-Axis"])
257 """
[257]258 self._abcissa = abcissa
[920]259 if self._data: self.plot(self._data)
[257]260 return
261
[710]262 def set_colors(self, colormap):
[377]263 """
[710]264 Set the colors to be used. The plotter will cycle through
265 these colors when lines are overlaid (stacking mode).
[1021]266 Parameters:
267 colormap: a list of colour names
[710]268 Example:
269 plotter.set_colors("red green blue")
270 # If for example four lines are overlaid e.g I Q U V
271 # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
272 # and 'V' will be 'red' again.
273 """
274 if isinstance(colormap,str):
275 colormap = colormap.split()
276 self._plotter.palette(0,colormap=colormap)
[920]277 if self._data: self.plot(self._data)
[710]278
[1021]279 def set_histogram(self, hist=True):
280 """
281 Enable/Disable histogram-like plotting.
282 Parameters:
283 hist: True (default) or False. The fisrt default
284 is taken from the .asaprc setting
285 plotter.histogram
286 """
[1023]287 self._hist = hist
[1021]288 if self._data: self.plot(self._data)
[1023]289
[710]290 def set_linestyles(self, linestyles):
291 """
[734]292 Set the linestyles to be used. The plotter will cycle through
293 these linestyles when lines are overlaid (stacking mode) AND
294 only one color has been set.
[710]295 Parameters:
296 linestyles: a list of linestyles to use.
297 'line', 'dashed', 'dotted', 'dashdot',
298 'dashdotdot' and 'dashdashdot' are
299 possible
300
301 Example:
302 plotter.set_colors("black")
303 plotter.set_linestyles("line dashed dotted dashdot")
304 # If for example four lines are overlaid e.g I Q U V
305 # 'I' will be 'solid', 'Q' will be 'dashed',
306 # U will be 'dotted' and 'V' will be 'dashdot'.
307 """
308 if isinstance(linestyles,str):
309 linestyles = linestyles.split()
310 self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
[920]311 if self._data: self.plot(self._data)
[710]312
313 def save(self, filename=None, orientation=None, dpi=None):
314 """
[377]315 Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
316 Parameters:
317 filename: The name of the output file. This is optional
318 and autodetects the image format from the file
319 suffix. If non filename is specified a file
320 called 'yyyymmdd_hhmmss.png' is created in the
321 current directory.
[709]322 orientation: optional parameter for postscript only (not eps).
323 'landscape', 'portrait' or None (default) are valid.
324 If None is choosen for 'ps' output, the plot is
325 automatically oriented to fill the page.
[710]326 dpi: The dpi of the output non-ps plot
[377]327 """
[709]328 self._plotter.save(filename,orientation,dpi)
[377]329 return
[709]330
[257]331
[920]332 def set_mask(self, mask=None, selection=None):
[525]333 """
[734]334 Set a plotting mask for a specific polarization.
335 This is useful for masking out "noise" Pangle outside a source.
336 Parameters:
[920]337 mask: a mask from scantable.create_mask
338 selection: the spectra to apply the mask to.
[734]339 Example:
[920]340 select = selector()
341 select.setpolstrings("Pangle")
342 plotter.set_mask(mymask, select)
[734]343 """
[710]344 if not self._data:
[920]345 msg = "Can only set mask after a first call to plot()"
[753]346 if rcParams['verbose']:
347 print msg
[762]348 return
[753]349 else:
[762]350 raise RuntimeError(msg)
[920]351 if len(mask):
352 if isinstance(mask, list) or isinstance(mask, tuple):
353 self._usermask = array(mask)
[710]354 else:
[920]355 self._usermask = mask
356 if mask is None and selection is None:
357 self._usermask = []
358 self._maskselection = None
359 if isinstance(selection, selector):
[947]360 self._maskselection = {'b': selection.get_beams(),
361 's': selection.get_scans(),
362 'i': selection.get_ifs(),
363 'p': selection.get_pols(),
[920]364 't': [] }
[710]365 else:
[920]366 self._maskselection = None
367 self.plot(self._data)
[710]368
[709]369 def _slice_indeces(self, data):
370 mn = self._minmaxx[0]
371 mx = self._minmaxx[1]
372 asc = data[0] < data[-1]
373 start=0
374 end = len(data)-1
375 inc = 1
376 if not asc:
377 start = len(data)-1
378 end = 0
379 inc = -1
380 # find min index
381 while data[start] < mn:
382 start+= inc
383 # find max index
384 while data[end] > mx:
385 end-=inc
386 end +=1
387 if start > end:
388 return end,start
389 return start,end
390
[710]391 def _reset(self):
[920]392 self._usermask = []
[710]393 self._usermaskspectra = None
[920]394 self.set_selection(None, False)
395
396 def _plot(self, scan):
[947]397 savesel = scan.get_selection()
398 sel = savesel + self._selection
399 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
400 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
401 order = [d0[self._panelling],d0[self._stacking]]
402 sel.set_order(order)
403 scan.set_selection(sel)
[920]404 d = {'b': scan.getbeam, 's': scan.getscan,
405 'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
406
[947]407 polmodes = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
[920]408 n,nstack = self._get_selected_n(scan)
[998]409 maxpanel, maxstack = 16,8
[920]410 if n > maxpanel or nstack > maxstack:
411 from asap import asaplog
412 msg ="Scan to be plotted contains more than %d selections.\n" \
413 "Selecting first %d selections..." % (maxpanel,maxpanel)
414 asaplog.push(msg)
415 print_log()
416 n = min(n,maxpanel)
[998]417 nstack = min(nstack,maxstack)
[920]418
419 if n > 1:
420 ganged = rcParams['plotter.ganged']
421 if self._rows and self._cols:
422 n = min(n,self._rows*self._cols)
423 self._plotter.set_panels(rows=self._rows,cols=self._cols,
424 nplots=n,ganged=ganged)
425 else:
426 self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
427 else:
428 self._plotter.set_panels()
429 r=0
430 nr = scan.nrow()
431 a0,b0 = -1,-1
432 allxlim = []
[1018]433 allylim = []
[920]434 newpanel=True
435 panelcount,stackcount = 0,0
[1002]436 while r < nr:
[920]437 a = d[self._panelling](r)
438 b = d[self._stacking](r)
439 if a > a0 and panelcount < n:
440 if n > 1:
441 self._plotter.subplot(panelcount)
442 self._plotter.palette(0)
443 #title
444 xlab = self._abcissa and self._abcissa[panelcount] \
445 or scan._getabcissalabel()
446 ylab = self._ordinate and self._ordinate[panelcount] \
447 or scan._get_ordinate_label()
448 self._plotter.set_axes('xlabel',xlab)
449 self._plotter.set_axes('ylabel',ylab)
450 lbl = self._get_label(scan, r, self._panelling, self._title)
451 if isinstance(lbl, list) or isinstance(lbl, tuple):
452 if 0 <= panelcount < len(lbl):
453 lbl = lbl[panelcount]
454 else:
455 # get default label
456 lbl = self._get_label(scan, r, self._panelling, None)
457 self._plotter.set_axes('title',lbl)
458 newpanel = True
459 stackcount =0
460 panelcount += 1
461 if (b > b0 or newpanel) and stackcount < nstack:
462 y = []
463 if len(polmodes):
464 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
465 else:
466 y = scan._getspectrum(r)
467 m = scan._getmask(r)
468 if self._maskselection and len(self._usermask) == len(m):
469 if d[self._stacking](r) in self._maskselection[self._stacking]:
470 m = logical_and(m, self._usermask)
471 x = scan._getabcissa(r)
[1087]472 from matplotlib.numerix import ma,logical_not,array
473 y = ma.MA.MaskedArray(y,mask=logical_not(array(m,copy=0)),copy=0)
[920]474 if self._minmaxx is not None:
475 s,e = self._slice_indeces(x)
476 x = x[s:e]
477 y = y[s:e]
[1096]478 if len(x) > 1024 and rcParams['plotter.decimate']:
479 fac = len(x)/1024
[920]480 x = x[::fac]
481 y = y[::fac]
482 llbl = self._get_label(scan, r, self._stacking, self._lmap)
483 if isinstance(llbl, list) or isinstance(llbl, tuple):
484 if 0 <= stackcount < len(llbl):
485 # use user label
486 llbl = llbl[stackcount]
487 else:
488 # get default label
489 llbl = self._get_label(scan, r, self._stacking, None)
490 self._plotter.set_line(label=llbl)
[1023]491 plotit = self._plotter.plot
492 if self._hist: plotit = self._plotter.hist
[1087]493 plotit(x,y)
[920]494 xlim= self._minmaxx or [min(x),max(x)]
495 allxlim += xlim
[1087]496 ylim= self._minmaxy or [ma.MA.minimum(y),ma.MA.maximum(y)]
[1018]497 allylim += ylim
[920]498 stackcount += 1
499 # last in colour stack -> autoscale x
500 if stackcount == nstack:
501 allxlim.sort()
502 self._plotter.axes.set_xlim([allxlim[0],allxlim[-1]])
503 # clear
504 allxlim =[]
505
506 newpanel = False
507 a0=a
508 b0=b
509 # ignore following rows
510 if (panelcount == n) and (stackcount == nstack):
[1018]511 # last panel -> autoscale y if ganged
512 if rcParams['plotter.ganged']:
513 allylim.sort()
514 self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
[998]515 break
[920]516 r+=1 # next row
[947]517 #reset the selector to the scantable's original
518 scan.set_selection(savesel)
[920]519
520 def set_selection(self, selection=None, refresh=True):
[947]521 self._selection = isinstance(selection,selector) and selection or selector()
[920]522 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
523 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
524 order = [d0[self._panelling],d0[self._stacking]]
[947]525 self._selection.set_order(order)
[920]526 if self._data and refresh: self.plot(self._data)
527
528 def _get_selected_n(self, scan):
529 d1 = {'b': scan.nbeam, 's': scan.nscan,
530 'i': scan.nif, 'p': scan.npol, 't': scan.ncycle }
[947]531 d2 = { 'b': len(self._selection.get_beams()),
532 's': len(self._selection.get_scans()),
533 'i': len(self._selection.get_ifs()),
534 'p': len(self._selection.get_pols()),
535 't': len(self._selection.get_cycles()) }
[920]536 n = d2[self._panelling] or d1[self._panelling]()
537 nstack = d2[self._stacking] or d1[self._stacking]()
538 return n,nstack
539
540 def _get_label(self, scan, row, mode, userlabel=None):
[947]541 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
[920]542 if len(pms):
543 poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
544 else:
545 poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
546 d = {'b': "Beam "+str(scan.getbeam(row)),
547 's': scan._getsourcename(row),
548 'i': "IF"+str(scan.getif(row)),
[964]549 'p': poleval,
[920]550 't': scan._gettime(row) }
551 return userlabel or d[mode]
Note: See TracBrowser for help on using the repository browser.