source: branches/newfiller/python/asapplotter.py@ 3037

Last change on this file since 3037 was 1798, checked in by Malte Marquarding, 14 years ago

merge -r1774:1797 from alma to newfiller

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.5 KB
Line 
1from asap import rcParams, print_log, print_log_dec
2from asap import selector, scantable
3from asap import asaplog
4import matplotlib.axes
5from matplotlib.font_manager import FontProperties
6from matplotlib.text import Text
7
8import re
9
10class asapplotter:
11 """
12 The ASAP plotter.
13 By default the plotter is set up to plot polarisations
14 'colour stacked' and scantables across panels.
15 Note:
16 Currenly it only plots 'spectra' not Tsys or
17 other variables.
18 """
19 def __init__(self, visible=None , **kwargs):
20 self._visible = rcParams['plotter.gui']
21 if visible is not None:
22 self._visible = visible
23 self._plotter = self._newplotter(**kwargs)
24 # additional tool bar
25 self._plotter.figmgr.casabar=self._newcasabar()
26
27 self._panelling = None
28 self._stacking = None
29 self.set_panelling()
30 self.set_stacking()
31 self._rows = None
32 self._cols = None
33 self._autoplot = False
34 self._minmaxx = None
35 self._minmaxy = None
36 self._datamask = None
37 self._data = None
38 self._lmap = None
39 self._title = None
40 self._ordinate = None
41 self._abcissa = None
42 self._abcunit = None
43 self._usermask = []
44 self._maskselection = None
45 self._selection = selector()
46 self._hist = rcParams['plotter.histogram']
47 self._fp = FontProperties()
48 self._panellayout = self.set_panellayout(refresh=False)
49
50 def _translate(self, instr):
51 keys = "s b i p t".split()
52 if isinstance(instr, str):
53 for key in keys:
54 if instr.lower().startswith(key):
55 return key
56 return None
57
58 def _newplotter(self, **kwargs):
59 backend=matplotlib.get_backend()
60 if not self._visible:
61 from asap.asaplot import asaplot
62 elif backend == 'TkAgg':
63 from asap.asaplotgui import asaplotgui as asaplot
64 elif backend == 'Qt4Agg':
65 from asap.asaplotgui_qt4 import asaplotgui as asaplot
66 elif backend == 'GTkAgg':
67 from asap.asaplotgui_gtk import asaplotgui as asaplot
68 else:
69 from asap.asaplot import asaplot
70 return asaplot(**kwargs)
71
72 def _newcasabar(self):
73 backend=matplotlib.get_backend()
74 if self._visible and backend == "TkAgg":
75 from asap.casatoolbar import CustomToolbarTkAgg
76 return CustomToolbarTkAgg(self)
77 else: return None
78
79 @print_log_dec
80 def plot(self, scan=None):
81 """
82 Plot a scantable.
83 Parameters:
84 scan: a scantable
85 Note:
86 If a scantable was specified in a previous call
87 to plot, no argument has to be given to 'replot'
88 NO checking is done that the abcissas of the scantable
89 are consistent e.g. all 'channel' or all 'velocity' etc.
90 """
91 if self._plotter.is_dead:
92 if hasattr(self._plotter.figmgr,'casabar'):
93 del self._plotter.figmgr.casabar
94 self._plotter = self._newplotter()
95 self._plotter.figmgr.casabar=self._newcasabar()
96 self._plotter.hold()
97 self._plotter.clear()
98 if not self._data and not scan:
99 msg = "Input is not a scantable"
100 if rcParams['verbose']:
101 #print msg
102 asaplog.push( msg )
103 print_log( 'ERROR' )
104 return
105 raise TypeError(msg)
106 if scan: self.set_data(scan,refresh=False)
107 self._plot(self._data)
108 if self._minmaxy is not None:
109 self._plotter.set_limits(ylim=self._minmaxy)
110 if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.enable_button()
111 self._plotter.release()
112 self._plotter.tidy()
113 self._plotter.show(hardrefresh=False)
114 print_log()
115 return
116
117 def gca(self):
118 return self._plotter.figure.gca()
119
120 def refresh(self):
121 """Do a soft refresh"""
122 self._plotter.figure.show()
123
124 def create_mask(self, nwin=1, panel=0, color=None):
125 """
126 Interactively define a mask.It retruns a mask that is equivalent to
127 the one created manually with scantable.create_mask.
128 Parameters:
129 nwin: The number of mask windows to create interactively
130 default is 1.
131 panel: Which panel to use for mask selection. This is useful
132 if different IFs are spread over panels (default 0)
133 """
134 if self._data is None:
135 return []
136 outmask = []
137 self._plotter.subplot(panel)
138 xmin, xmax = self._plotter.axes.get_xlim()
139 marg = 0.05*(xmax-xmin)
140 self._plotter.axes.set_xlim(xmin-marg, xmax+marg)
141 self.refresh()
142
143 def cleanup(lines=False, texts=False, refresh=False):
144 if lines:
145 del self._plotter.axes.lines[-1]
146 if texts:
147 del self._plotter.axes.texts[-1]
148 if refresh:
149 self.refresh()
150
151 for w in xrange(nwin):
152 wpos = []
153 self.text(0.05,1.0, "Add start boundary",
154 coords="relative", fontsize=10)
155 point = self._plotter.get_point()
156 cleanup(texts=True)
157 if point is None:
158 continue
159 wpos.append(point[0])
160 self.axvline(wpos[0], color=color)
161 self.text(0.05,1.0, "Add end boundary", coords="relative", fontsize=10)
162 point = self._plotter.get_point()
163 cleanup(texts=True, lines=True)
164 if point is None:
165 self.refresh()
166 continue
167 wpos.append(point[0])
168 self.axvspan(wpos[0], wpos[1], alpha=0.1,
169 edgecolor=color, facecolor=color)
170 ymin, ymax = self._plotter.axes.get_ylim()
171 outmask.append(wpos)
172
173 self._plotter.axes.set_xlim(xmin, xmax)
174 self.refresh()
175 if len(outmask) > 0:
176 return self._data.create_mask(*outmask)
177 return []
178
179 # forwards to matplotlib axes
180 def text(self, *args, **kwargs):
181 if kwargs.has_key("interactive"):
182 if kwargs.pop("interactive"):
183 pos = self._plotter.get_point()
184 args = tuple(pos)+args
185 self._axes_callback("text", *args, **kwargs)
186
187 text.__doc__ = matplotlib.axes.Axes.text.__doc__
188
189 def arrow(self, *args, **kwargs):
190 if kwargs.has_key("interactive"):
191 if kwargs.pop("interactive"):
192 pos = self._plotter.get_region()
193 dpos = (pos[0][0], pos[0][1],
194 pos[1][0]-pos[0][0],
195 pos[1][1] - pos[0][1])
196 args = dpos + args
197 self._axes_callback("arrow", *args, **kwargs)
198
199 arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
200
201 def annotate(self, text, xy=None, xytext=None, **kwargs):
202 if kwargs.has_key("interactive"):
203 if kwargs.pop("interactive"):
204 xy = self._plotter.get_point()
205 xytext = self._plotter.get_point()
206 if not kwargs.has_key("arrowprops"):
207 kwargs["arrowprops"] = dict(arrowstyle="->")
208 self._axes_callback("annotate", text, xy, xytext, **kwargs)
209
210 annotate.__doc__ = matplotlib.axes.Axes.annotate.__doc__
211
212 def axvline(self, *args, **kwargs):
213 if kwargs.has_key("interactive"):
214 if kwargs.pop("interactive"):
215 pos = self._plotter.get_point()
216 args = (pos[0],)+args
217 self._axes_callback("axvline", *args, **kwargs)
218
219 axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
220
221 def axhline(self, *args, **kwargs):
222 if kwargs.has_key("interactive"):
223 if kwargs.pop("interactive"):
224 pos = self._plotter.get_point()
225 args = (pos[1],)+args
226 self._axes_callback("axhline", *args, **kwargs)
227
228 axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
229
230 def axvspan(self, *args, **kwargs):
231 if kwargs.has_key("interactive"):
232 if kwargs.pop("interactive"):
233 pos = self._plotter.get_region()
234 dpos = (pos[0][0], pos[1][0])
235 args = dpos + args
236 self._axes_callback("axvspan", *args, **kwargs)
237 # hack to preventy mpl from redrawing the patch
238 # it seem to convert the patch into lines on every draw.
239 # This doesn't happen in a test script???
240 #del self._plotter.axes.patches[-1]
241
242 axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
243
244 def axhspan(self, *args, **kwargs):
245 if kwargs.has_key("interactive"):
246 if kwargs.pop("interactive"):
247 pos = self._plotter.get_region()
248 dpos = (pos[0][1], pos[1][1])
249 args = dpos + args
250 self._axes_callback("axhspan", *args, **kwargs)
251 # hack to preventy mpl from redrawing the patch
252 # it seem to convert the patch into lines on every draw.
253 # This doesn't happen in a test script???
254 #del self._plotter.axes.patches[-1]
255
256 axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
257
258 def _axes_callback(self, axesfunc, *args, **kwargs):
259 panel = 0
260 if kwargs.has_key("panel"):
261 panel = kwargs.pop("panel")
262 coords = None
263 if kwargs.has_key("coords"):
264 coords = kwargs.pop("coords")
265 if coords.lower() == 'world':
266 kwargs["transform"] = self._plotter.axes.transData
267 elif coords.lower() == 'relative':
268 kwargs["transform"] = self._plotter.axes.transAxes
269 self._plotter.subplot(panel)
270 self._plotter.axes.set_autoscale_on(False)
271 getattr(self._plotter.axes, axesfunc)(*args, **kwargs)
272 self._plotter.show(False)
273 self._plotter.axes.set_autoscale_on(True)
274 # end matplotlib.axes fowarding functions
275
276 def set_data(self, scan, refresh=True):
277 """
278 Set a scantable to plot.
279 Parameters:
280 scan: a scantable
281 refresh: True (default) or False. If True, the plot is
282 replotted based on the new parameter setting(s).
283 Otherwise,the parameter(s) are set without replotting.
284 Note:
285 The user specified masks and data selections will be reset
286 if a new scantable is set. This method should be called before
287 setting data selections (set_selection) and/or masks (set_mask).
288 """
289 from asap import scantable
290 if isinstance(scan, scantable):
291 if self._data is not None:
292 if scan != self._data:
293 self._data = scan
294 # reset
295 self._reset()
296 msg = "A new scantable is set to the plotter. The masks and data selections are reset."
297 asaplog.push( msg )
298 print_log( 'INFO' )
299 else:
300 self._data = scan
301 self._reset()
302 else:
303 msg = "Input is not a scantable"
304 if rcParams['verbose']:
305 #print msg
306 asaplog.push( msg )
307 print_log( 'ERROR' )
308 return
309 raise TypeError(msg)
310
311 # ranges become invalid when unit changes
312 if self._abcunit and self._abcunit != self._data.get_unit():
313 self._minmaxx = None
314 self._minmaxy = None
315 self._abcunit = self._data.get_unit()
316 self._datamask = None
317 if refresh: self.plot()
318
319
320 def set_mode(self, stacking=None, panelling=None, refresh=True):
321 """
322 Set the plots look and feel, i.e. what you want to see on the plot.
323 Parameters:
324 stacking: tell the plotter which variable to plot
325 as line colour overlays (default 'pol')
326 panelling: tell the plotter which variable to plot
327 across multiple panels (default 'scan'
328 refresh: True (default) or False. If True, the plot is
329 replotted based on the new parameter setting(s).
330 Otherwise,the parameter(s) are set without replotting.
331 Note:
332 Valid modes are:
333 'beam' 'Beam' 'b': Beams
334 'if' 'IF' 'i': IFs
335 'pol' 'Pol' 'p': Polarisations
336 'scan' 'Scan' 's': Scans
337 'time' 'Time' 't': Times
338 """
339 msg = "Invalid mode"
340 if not self.set_panelling(panelling) or \
341 not self.set_stacking(stacking):
342 if rcParams['verbose']:
343 #print msg
344 asaplog.push( msg )
345 print_log( 'ERROR' )
346 return
347 else:
348 raise TypeError(msg)
349 if refresh and self._data: self.plot(self._data)
350 return
351
352 def set_panelling(self, what=None):
353 mode = what
354 if mode is None:
355 mode = rcParams['plotter.panelling']
356 md = self._translate(mode)
357 if md:
358 self._panelling = md
359 self._title = None
360 return True
361 return False
362
363 def set_layout(self,rows=None,cols=None,refresh=True):
364 """
365 Set the multi-panel layout, i.e. how many rows and columns plots
366 are visible.
367 Parameters:
368 rows: The number of rows of plots
369 cols: The number of columns of plots
370 refresh: True (default) or False. If True, the plot is
371 replotted based on the new parameter setting(s).
372 Otherwise,the parameter(s) are set without replotting.
373 Note:
374 If no argument is given, the potter reverts to its auto-plot
375 behaviour.
376 """
377 self._rows = rows
378 self._cols = cols
379 if refresh and self._data: self.plot(self._data)
380 return
381
382 def set_stacking(self, what=None):
383 mode = what
384 if mode is None:
385 mode = rcParams['plotter.stacking']
386 md = self._translate(mode)
387 if md:
388 self._stacking = md
389 self._lmap = None
390 return True
391 return False
392
393 def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True):
394 """
395 Set the range of interest on the abcissa of the plot
396 Parameters:
397 [x,y]start,[x,y]end: The start and end points of the 'zoom' window
398 refresh: True (default) or False. If True, the plot is
399 replotted based on the new parameter setting(s).
400 Otherwise,the parameter(s) are set without replotting.
401 Note:
402 These become non-sensical when the unit changes.
403 use plotter.set_range() without parameters to reset
404
405 """
406 if xstart is None and xend is None:
407 self._minmaxx = None
408 else:
409 self._minmaxx = [xstart,xend]
410 if ystart is None and yend is None:
411 self._minmaxy = None
412 else:
413 self._minmaxy = [ystart,yend]
414 if refresh and self._data: self.plot(self._data)
415 return
416
417 def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
418 """
419 Specify a mapping for the legend instead of using the default
420 indices:
421 Parameters:
422 mp: a list of 'strings'. This should have the same length
423 as the number of elements on the legend and then maps
424 to the indeces in order. It is possible to uses latex
425 math expression. These have to be enclosed in r'',
426 e.g. r'$x^{2}$'
427 fontsize: The font size of the label (default None)
428 mode: where to display the legend
429 Any other value for loc else disables the legend:
430 0: auto
431 1: upper right
432 2: upper left
433 3: lower left
434 4: lower right
435 5: right
436 6: center left
437 7: center right
438 8: lower center
439 9: upper center
440 10: center
441 refresh: True (default) or False. If True, the plot is
442 replotted based on the new parameter setting(s).
443 Otherwise,the parameter(s) are set without replotting.
444
445 Example:
446 If the data has two IFs/rest frequencies with index 0 and 1
447 for CO and SiO:
448 plotter.set_stacking('i')
449 plotter.set_legend(['CO','SiO'])
450 plotter.plot()
451 plotter.set_legend([r'$^{12}CO$', r'SiO'])
452 """
453 self._lmap = mp
454 self._plotter.legend(mode)
455 if isinstance(fontsize, int):
456 from matplotlib import rc as rcp
457 rcp('legend', fontsize=fontsize)
458 if refresh and self._data: self.plot(self._data)
459 return
460
461 def set_title(self, title=None, fontsize=None, refresh=True):
462 """
463 Set the title of the plot. If multiple panels are plotted,
464 multiple titles have to be specified.
465 Parameters:
466 refresh: True (default) or False. If True, the plot is
467 replotted based on the new parameter setting(s).
468 Otherwise,the parameter(s) are set without replotting.
469 Example:
470 # two panels are visible on the plotter
471 plotter.set_title(["First Panel","Second Panel"])
472 """
473 self._title = title
474 if isinstance(fontsize, int):
475 from matplotlib import rc as rcp
476 rcp('axes', titlesize=fontsize)
477 if refresh and self._data: self.plot(self._data)
478 return
479
480 def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
481 """
482 Set the y-axis label of the plot. If multiple panels are plotted,
483 multiple labels have to be specified.
484 Parameters:
485 ordinate: a list of ordinate labels. None (default) let
486 data determine the labels
487 refresh: True (default) or False. If True, the plot is
488 replotted based on the new parameter setting(s).
489 Otherwise,the parameter(s) are set without replotting.
490 Example:
491 # two panels are visible on the plotter
492 plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
493 """
494 self._ordinate = ordinate
495 if isinstance(fontsize, int):
496 from matplotlib import rc as rcp
497 rcp('axes', labelsize=fontsize)
498 rcp('ytick', labelsize=fontsize)
499 if refresh and self._data: self.plot(self._data)
500 return
501
502 def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
503 """
504 Set the x-axis label of the plot. If multiple panels are plotted,
505 multiple labels have to be specified.
506 Parameters:
507 abcissa: a list of abcissa labels. None (default) let
508 data determine the labels
509 refresh: True (default) or False. If True, the plot is
510 replotted based on the new parameter setting(s).
511 Otherwise,the parameter(s) are set without replotting.
512 Example:
513 # two panels are visible on the plotter
514 plotter.set_ordinate(["First X-Axis","Second X-Axis"])
515 """
516 self._abcissa = abcissa
517 if isinstance(fontsize, int):
518 from matplotlib import rc as rcp
519 rcp('axes', labelsize=fontsize)
520 rcp('xtick', labelsize=fontsize)
521 if refresh and self._data: self.plot(self._data)
522 return
523
524 def set_colors(self, colmap, refresh=True):
525 """
526 Set the colours to be used. The plotter will cycle through
527 these colours when lines are overlaid (stacking mode).
528 Parameters:
529 colmap: a list of colour names
530 refresh: True (default) or False. If True, the plot is
531 replotted based on the new parameter setting(s).
532 Otherwise,the parameter(s) are set without replotting.
533 Example:
534 plotter.set_colors("red green blue")
535 # If for example four lines are overlaid e.g I Q U V
536 # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
537 # and 'V' will be 'red' again.
538 """
539 if isinstance(colmap,str):
540 colmap = colmap.split()
541 self._plotter.palette(0, colormap=colmap)
542 if refresh and self._data: self.plot(self._data)
543
544 # alias for english speakers
545 set_colours = set_colors
546
547 def set_histogram(self, hist=True, linewidth=None, refresh=True):
548 """
549 Enable/Disable histogram-like plotting.
550 Parameters:
551 hist: True (default) or False. The fisrt default
552 is taken from the .asaprc setting
553 plotter.histogram
554 refresh: True (default) or False. If True, the plot is
555 replotted based on the new parameter setting(s).
556 Otherwise,the parameter(s) are set without replotting.
557 """
558 self._hist = hist
559 if isinstance(linewidth, float) or isinstance(linewidth, int):
560 from matplotlib import rc as rcp
561 rcp('lines', linewidth=linewidth)
562 if refresh and self._data: self.plot(self._data)
563
564 def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
565 """
566 Set the linestyles to be used. The plotter will cycle through
567 these linestyles when lines are overlaid (stacking mode) AND
568 only one color has been set.
569 Parameters:
570 linestyles: a list of linestyles to use.
571 'line', 'dashed', 'dotted', 'dashdot',
572 'dashdotdot' and 'dashdashdot' are
573 possible
574 refresh: True (default) or False. If True, the plot is
575 replotted based on the new parameter setting(s).
576 Otherwise,the parameter(s) are set without replotting.
577 Example:
578 plotter.set_colors("black")
579 plotter.set_linestyles("line dashed dotted dashdot")
580 # If for example four lines are overlaid e.g I Q U V
581 # 'I' will be 'solid', 'Q' will be 'dashed',
582 # U will be 'dotted' and 'V' will be 'dashdot'.
583 """
584 if isinstance(linestyles,str):
585 linestyles = linestyles.split()
586 self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
587 if isinstance(linewidth, float) or isinstance(linewidth, int):
588 from matplotlib import rc as rcp
589 rcp('lines', linewidth=linewidth)
590 if refresh and self._data: self.plot(self._data)
591
592 def set_font(self, refresh=True,**kwargs):
593 """
594 Set font properties.
595 Parameters:
596 family: one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
597 style: one of 'normal' (or 'roman'), 'italic' or 'oblique'
598 weight: one of 'normal or 'bold'
599 size: the 'general' font size, individual elements can be adjusted
600 seperately
601 refresh: True (default) or False. If True, the plot is
602 replotted based on the new parameter setting(s).
603 Otherwise,the parameter(s) are set without replotting.
604 """
605 from matplotlib import rc as rcp
606 fdict = {}
607 for k,v in kwargs.iteritems():
608 if v:
609 fdict[k] = v
610 self._fp = FontProperties(**fdict)
611 if refresh and self._data: self.plot(self._data)
612
613 def set_panellayout(self,layout=[],refresh=True):
614 """
615 Set the layout of subplots.
616 Parameters:
617 layout: a list of subplots layout in figure coordinate (0-1),
618 i.e., fraction of the figure width or height.
619 The order of elements should be:
620 [left, bottom, right, top, horizontal space btw panels,
621 vertical space btw panels].
622 refresh: True (default) or False. If True, the plot is
623 replotted based on the new parameter setting(s).
624 Otherwise,the parameter(s) are set without replotting.
625 Note
626 * When layout is not specified, the values are reset to the defaults
627 of matplotlib.
628 * If any element is set to be None, the current value is adopted.
629 """
630 if layout == []: self._panellayout=self._reset_panellayout()
631 else:
632 self._panellayout=[None]*6
633 self._panellayout[0:len(layout)]=layout
634 #print "panel layout set to ",self._panellayout
635 if refresh and self._data: self.plot(self._data)
636
637 def _reset_panellayout(self):
638 ks=map(lambda x: 'figure.subplot.'+x,
639 ['left','bottom','right','top','hspace','wspace'])
640 return map(matplotlib.rcParams.get,ks)
641
642 def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
643 location=None):
644 """
645 Plot a line catalog.
646 Parameters:
647 linecat: the linecatalog to plot
648 doppler: the velocity shift to apply to the frequencies
649 deltachan: the number of channels to include each side of the
650 line to determine a local maximum/minimum
651 rotate: the rotation (in degrees) )for the text label (default 90.0)
652 location: the location of the line annotation from the 'top',
653 'bottom' or alternate (None - the default)
654 Notes:
655 If the spectrum is flagged no line will be drawn in that location.
656 """
657 if not self._data:
658 raise RuntimeError("No scantable has been plotted yet.")
659 from asap._asap import linecatalog
660 if not isinstance(linecat, linecatalog):
661 raise ValueError("'linecat' isn't of type linecatalog.")
662 if not self._data.get_unit().endswith("Hz"):
663 raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
664 from numpy import ma
665 for j in range(len(self._plotter.subplots)):
666 self._plotter.subplot(j)
667 lims = self._plotter.axes.get_xlim()
668 for row in range(linecat.nrow()):
669 # get_frequency returns MHz
670 base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
671 restf = linecat.get_frequency(row)/base[self._data.get_unit()]
672 c = 299792.458
673 freq = restf*(1.0-doppler/c)
674 if lims[0] < freq < lims[1]:
675 if location is None:
676 loc = 'bottom'
677 if row%2: loc='top'
678 else: loc = location
679 maxys = []
680 for line in self._plotter.axes.lines:
681 v = line._x
682 asc = v[0] < v[-1]
683
684 idx = None
685 if not asc:
686 if v[len(v)-1] <= freq <= v[0]:
687 i = len(v)-1
688 while i>=0 and v[i] < freq:
689 idx = i
690 i-=1
691 else:
692 if v[0] <= freq <= v[len(v)-1]:
693 i = 0
694 while i<len(v) and v[i] < freq:
695 idx = i
696 i+=1
697 if idx is not None:
698 lower = idx - deltachan
699 upper = idx + deltachan
700 if lower < 0: lower = 0
701 if upper > len(v): upper = len(v)
702 s = slice(lower, upper)
703 y = line._y[s]
704 maxy = ma.maximum(y)
705 if isinstance( maxy, float):
706 maxys.append(maxy)
707 if len(maxys):
708 peak = max(maxys)
709 if peak > self._plotter.axes.get_ylim()[1]:
710 loc = 'bottom'
711 else:
712 continue
713 self._plotter.vline_with_label(freq, peak,
714 linecat.get_name(row),
715 location=loc, rotate=rotate)
716 self._plotter.show(hardrefresh=False)
717
718
719 def save(self, filename=None, orientation=None, dpi=None):
720 """
721 Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
722 Parameters:
723 filename: The name of the output file. This is optional
724 and autodetects the image format from the file
725 suffix. If non filename is specified a file
726 called 'yyyymmdd_hhmmss.png' is created in the
727 current directory.
728 orientation: optional parameter for postscript only (not eps).
729 'landscape', 'portrait' or None (default) are valid.
730 If None is choosen for 'ps' output, the plot is
731 automatically oriented to fill the page.
732 dpi: The dpi of the output non-ps plot
733 """
734 self._plotter.save(filename,orientation,dpi)
735 return
736
737
738 def set_mask(self, mask=None, selection=None, refresh=True):
739 """
740 Set a plotting mask for a specific polarization.
741 This is useful for masking out "noise" Pangle outside a source.
742 Parameters:
743 mask: a mask from scantable.create_mask
744 selection: the spectra to apply the mask to.
745 refresh: True (default) or False. If True, the plot is
746 replotted based on the new parameter setting(s).
747 Otherwise,the parameter(s) are set without replotting.
748 Example:
749 select = selector()
750 select.setpolstrings("Pangle")
751 plotter.set_mask(mymask, select)
752 """
753 if not self._data:
754 msg = "Can only set mask after a first call to plot()"
755 if rcParams['verbose']:
756 #print msg
757 asaplog.push( msg )
758 print_log( 'ERROR' )
759 return
760 else:
761 raise RuntimeError(msg)
762 if len(mask):
763 if isinstance(mask, list) or isinstance(mask, tuple):
764 self._usermask = array(mask)
765 else:
766 self._usermask = mask
767 if mask is None and selection is None:
768 self._usermask = []
769 self._maskselection = None
770 if isinstance(selection, selector):
771 self._maskselection = {'b': selection.get_beams(),
772 's': selection.get_scans(),
773 'i': selection.get_ifs(),
774 'p': selection.get_pols(),
775 't': [] }
776 else:
777 self._maskselection = None
778 if refresh: self.plot(self._data)
779
780 def _slice_indeces(self, data):
781 mn = self._minmaxx[0]
782 mx = self._minmaxx[1]
783 asc = data[0] < data[-1]
784 start=0
785 end = len(data)-1
786 inc = 1
787 if not asc:
788 start = len(data)-1
789 end = 0
790 inc = -1
791 # find min index
792 #while start > 0 and data[start] < mn:
793 # start+= inc
794 minind=start
795 for ind in xrange(start,end+inc,inc):
796 if data[ind] > mn: break
797 minind=ind
798 # find max index
799 #while end > 0 and data[end] > mx:
800 # end-=inc
801 #if end > 0: end +=1
802 maxind=end
803 for ind in xrange(end,start-inc,-inc):
804 if data[ind] < mx: break
805 maxind=ind
806 start=minind
807 end=maxind
808 if start > end:
809 return end,start+1
810 elif start < end:
811 return start,end+1
812 else:
813 return start,end
814
815 def _reset(self):
816 self._usermask = []
817 self._usermaskspectra = None
818 self.set_selection(None, False)
819
820 def _plot(self, scan):
821 savesel = scan.get_selection()
822 sel = savesel + self._selection
823 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
824 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
825 order = [d0[self._panelling],d0[self._stacking]]
826 sel.set_order(order)
827 scan.set_selection(sel)
828 d = {'b': scan.getbeam, 's': scan.getscan,
829 'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
830
831 polmodes = dict(zip(self._selection.get_pols(),
832 self._selection.get_poltypes()))
833 # this returns either a tuple of numbers or a length (ncycles)
834 # convert this into lengths
835 n0,nstack0 = self._get_selected_n(scan)
836 if isinstance(n0, int): n = n0
837 else: n = len(n0)
838 if isinstance(nstack0, int): nstack = nstack0
839 else: nstack = len(nstack0)
840 maxpanel, maxstack = 16,16
841 if n > maxpanel or nstack > maxstack:
842 maxn = 0
843 if nstack > maxstack: maxn = maxstack
844 if n > maxpanel: maxn = maxpanel
845 msg ="Scan to be plotted contains more than %d selections.\n" \
846 "Selecting first %d selections..." % (maxn, maxn)
847 asaplog.push(msg)
848 print_log('WARN')
849 n = min(n,maxpanel)
850 nstack = min(nstack,maxstack)
851 if n > 1:
852 ganged = rcParams['plotter.ganged']
853 if self._panelling == 'i':
854 ganged = False
855 if self._rows and self._cols:
856 n = min(n,self._rows*self._cols)
857 self._plotter.set_panels(rows=self._rows,cols=self._cols,
858# nplots=n,ganged=ganged)
859 nplots=n,layout=self._panellayout,ganged=ganged)
860 else:
861# self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
862 self._plotter.set_panels(rows=n,cols=0,nplots=n,layout=self._panellayout,ganged=ganged)
863 else:
864# self._plotter.set_panels()
865 self._plotter.set_panels(layout=self._panellayout)
866 r=0
867 nr = scan.nrow()
868 a0,b0 = -1,-1
869 allxlim = []
870 allylim = []
871 newpanel=True
872 panelcount,stackcount = 0,0
873 while r < nr:
874 a = d[self._panelling](r)
875 b = d[self._stacking](r)
876 if a > a0 and panelcount < n:
877 if n > 1:
878 self._plotter.subplot(panelcount)
879 self._plotter.palette(0)
880 #title
881 xlab = self._abcissa and self._abcissa[panelcount] \
882 or scan._getabcissalabel()
883 ylab = self._ordinate and self._ordinate[panelcount] \
884 or scan._get_ordinate_label()
885 self._plotter.set_axes('xlabel', xlab)
886 self._plotter.set_axes('ylabel', ylab)
887 lbl = self._get_label(scan, r, self._panelling, self._title)
888 if isinstance(lbl, list) or isinstance(lbl, tuple):
889 if 0 <= panelcount < len(lbl):
890 lbl = lbl[panelcount]
891 else:
892 # get default label
893 lbl = self._get_label(scan, r, self._panelling, None)
894 self._plotter.set_axes('title',lbl)
895 newpanel = True
896 stackcount =0
897 panelcount += 1
898 if (b > b0 or newpanel) and stackcount < nstack:
899 y = []
900 if len(polmodes):
901 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
902 else:
903 y = scan._getspectrum(r)
904 m = scan._getmask(r)
905 from numpy import logical_not, logical_and
906 if self._maskselection and len(self._usermask) == len(m):
907 if d[self._stacking](r) in self._maskselection[self._stacking]:
908 m = logical_and(m, self._usermask)
909 x = scan._getabcissa(r)
910 from numpy import ma, array
911 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
912 if self._minmaxx is not None:
913 s,e = self._slice_indeces(x)
914 x = x[s:e]
915 y = y[s:e]
916 if len(x) > 1024 and rcParams['plotter.decimate']:
917 fac = len(x)/1024
918 x = x[::fac]
919 y = y[::fac]
920 llbl = self._get_label(scan, r, self._stacking, self._lmap)
921 if isinstance(llbl, list) or isinstance(llbl, tuple):
922 if 0 <= stackcount < len(llbl):
923 # use user label
924 llbl = llbl[stackcount]
925 else:
926 # get default label
927 llbl = self._get_label(scan, r, self._stacking, None)
928 self._plotter.set_line(label=llbl)
929 plotit = self._plotter.plot
930 if self._hist: plotit = self._plotter.hist
931 if len(x) > 0:
932 plotit(x,y)
933 xlim= self._minmaxx or [min(x),max(x)]
934 allxlim += xlim
935 ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
936 allylim += ylim
937 else:
938 xlim = self._minmaxx or []
939 allxlim += xlim
940 ylim= self._minmaxy or []
941 allylim += ylim
942 stackcount += 1
943 # last in colour stack -> autoscale x
944 if stackcount == nstack and len(allxlim) > 0:
945 allxlim.sort()
946 self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
947 # clear
948 allxlim =[]
949
950 newpanel = False
951 a0=a
952 b0=b
953 # ignore following rows
954 if (panelcount == n) and (stackcount == nstack):
955 # last panel -> autoscale y if ganged
956 if rcParams['plotter.ganged'] and len(allylim) > 0:
957 allylim.sort()
958 self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
959 break
960 r+=1 # next row
961 #reset the selector to the scantable's original
962 scan.set_selection(savesel)
963
964 #temporary switch-off for older matplotlib
965 #if self._fp is not None:
966 if self._fp is not None and getattr(self._plotter.figure,'findobj',False):
967 for o in self._plotter.figure.findobj(Text):
968 o.set_fontproperties(self._fp)
969
970 def set_selection(self, selection=None, refresh=True, **kw):
971 """
972 Parameters:
973 selection: a selector object (default unset the selection)
974 refresh: True (default) or False. If True, the plot is
975 replotted based on the new parameter setting(s).
976 Otherwise,the parameter(s) are set without replotting.
977 """
978 if selection is None:
979 # reset
980 if len(kw) == 0:
981 self._selection = selector()
982 else:
983 # try keywords
984 for k in kw:
985 if k not in selector.fields:
986 raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
987 self._selection = selector(**kw)
988 elif isinstance(selection, selector):
989 self._selection = selection
990 else:
991 raise TypeError("'selection' is not of type selector")
992
993 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
994 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
995 order = [d0[self._panelling],d0[self._stacking]]
996 self._selection.set_order(order)
997 if refresh and self._data: self.plot(self._data)
998
999 def _get_selected_n(self, scan):
1000 d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
1001 'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle }
1002 d2 = { 'b': self._selection.get_beams(),
1003 's': self._selection.get_scans(),
1004 'i': self._selection.get_ifs(),
1005 'p': self._selection.get_pols(),
1006 't': self._selection.get_cycles() }
1007 n = d2[self._panelling] or d1[self._panelling]()
1008 nstack = d2[self._stacking] or d1[self._stacking]()
1009 return n,nstack
1010
1011 def _get_label(self, scan, row, mode, userlabel=None):
1012 if isinstance(userlabel, list) and len(userlabel) == 0:
1013 userlabel = " "
1014 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
1015 if len(pms):
1016 poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
1017 else:
1018 poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
1019 d = {'b': "Beam "+str(scan.getbeam(row)),
1020 #'s': scan._getsourcename(row),
1021 's': "Scan "+str(scan.getscan(row))+\
1022 " ("+str(scan._getsourcename(row))+")",
1023 'i': "IF"+str(scan.getif(row)),
1024 'p': poleval,
1025 't': str(scan.get_time(row)) }
1026 return userlabel or d[mode]
1027
1028 def plotazel(self, scan=None, outfile=None):
1029 #def plotazel(self):
1030 """
1031 plot azimuth and elevation versus time of a scantable
1032 """
1033 from matplotlib import pylab as PL
1034 from matplotlib.dates import DateFormatter, timezone
1035 from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
1036 from matplotlib.ticker import MultipleLocator
1037 from numpy import array, pi
1038 self._data = scan
1039 self._outfile = outfile
1040 dates = self._data.get_time(asdatetime=True)
1041 t = PL.date2num(dates)
1042 tz = timezone('UTC')
1043 PL.cla()
1044 PL.ioff()
1045 PL.clf()
1046 # Adjust subplot layouts
1047 if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
1048 lef, bot, rig, top, wsp, hsp = self._panellayout
1049 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1050 wspace=wsp,hspace=hsp)
1051
1052 tdel = max(t) - min(t)
1053 ax = PL.subplot(2,1,1)
1054 el = array(self._data.get_elevation())*180./pi
1055 PL.ylabel('El [deg.]')
1056 dstr = dates[0].strftime('%Y/%m/%d')
1057 if tdel > 1.0:
1058 dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
1059 dstr = dstr + " - " + dstr2
1060 majloc = DayLocator()
1061 minloc = HourLocator(range(0,23,12))
1062 timefmt = DateFormatter("%b%d")
1063 elif tdel > 24./60.:
1064 timefmt = DateFormatter('%H:%M')
1065 majloc = HourLocator()
1066 minloc = MinuteLocator(30)
1067 else:
1068 timefmt = DateFormatter('%H:%M')
1069 majloc = MinuteLocator(interval=5)
1070 minloc = SecondLocator(30)
1071
1072 PL.title(dstr)
1073 if tdel == 0.0:
1074 th = (t - PL.floor(t))*24.0
1075 PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
1076 else:
1077 PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
1078 #ax.grid(True)
1079 ax.xaxis.set_major_formatter(timefmt)
1080 ax.xaxis.set_major_locator(majloc)
1081 ax.xaxis.set_minor_locator(minloc)
1082 ax.yaxis.grid(True)
1083 yloc = MultipleLocator(30)
1084 ax.set_ylim(0,90)
1085 ax.yaxis.set_major_locator(yloc)
1086 if tdel > 1.0:
1087 labels = ax.get_xticklabels()
1088 # PL.setp(labels, fontsize=10, rotation=45)
1089 PL.setp(labels, fontsize=10)
1090
1091 # Az plot
1092 az = array(self._data.get_azimuth())*180./pi
1093 if min(az) < 0:
1094 for irow in range(len(az)):
1095 if az[irow] < 0: az[irow] += 360.0
1096
1097 ax2 = PL.subplot(2,1,2)
1098 #PL.xlabel('Time (UT [hour])')
1099 PL.ylabel('Az [deg.]')
1100 if tdel == 0.0:
1101 PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
1102 else:
1103 PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
1104 ax2.xaxis.set_major_formatter(timefmt)
1105 ax2.xaxis.set_major_locator(majloc)
1106 ax2.xaxis.set_minor_locator(minloc)
1107 #ax2.grid(True)
1108 ax2.set_ylim(0,360)
1109 ax2.yaxis.grid(True)
1110 #hfmt = DateFormatter('%H')
1111 #hloc = HourLocator()
1112 yloc = MultipleLocator(60)
1113 ax2.yaxis.set_major_locator(yloc)
1114 if tdel > 1.0:
1115 labels = ax2.get_xticklabels()
1116 PL.setp(labels, fontsize=10)
1117 PL.xlabel('Time (UT [day])')
1118 else:
1119 PL.xlabel('Time (UT [hour])')
1120
1121 PL.ion()
1122 PL.draw()
1123 if (self._outfile is not None):
1124 PL.savefig(self._outfile)
1125
1126 def plotpointing(self, scan=None, outfile=None):
1127 #def plotpointing(self):
1128 """
1129 plot telescope pointings
1130 """
1131 from matplotlib import pylab as PL
1132 from numpy import array, pi
1133 self._data = scan
1134 self._outfile = outfile
1135 dir = array(self._data.get_directionval()).transpose()
1136 ra = dir[0]*180./pi
1137 dec = dir[1]*180./pi
1138 PL.cla()
1139 #PL.ioff()
1140 PL.clf()
1141 # Adjust subplot layouts
1142 if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
1143 lef, bot, rig, top, wsp, hsp = self._panellayout
1144 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1145 wspace=wsp,hspace=hsp)
1146 ax = PL.gca()
1147 #ax = PL.axes([0.1,0.1,0.8,0.8])
1148 #ax = PL.axes([0.1,0.1,0.8,0.8])
1149 ax.set_aspect('equal')
1150 PL.plot(ra, dec, 'b,')
1151 PL.xlabel('RA [deg.]')
1152 PL.ylabel('Declination [deg.]')
1153 PL.title('Telescope pointings')
1154 [xmin,xmax,ymin,ymax] = PL.axis()
1155 PL.axis([xmax,xmin,ymin,ymax])
1156 #PL.ion()
1157 PL.draw()
1158 if (self._outfile is not None):
1159 PL.savefig(self._outfile)
1160
1161 # plot total power data
1162 # plotting in time is not yet implemented..
1163 def plottp(self, scan=None, outfile=None):
1164 if self._plotter.is_dead:
1165 if hasattr(self._plotter.figmgr,'casabar'):
1166 del self._plotter.figmgr.casabar
1167 self._plotter = self._newplotter()
1168 self._plotter.figmgr.casabar=self._newcasabar()
1169 self._plotter.hold()
1170 self._plotter.clear()
1171 from asap import scantable
1172 if not self._data and not scan:
1173 msg = "Input is not a scantable"
1174 if rcParams['verbose']:
1175 #print msg
1176 asaplog.push( msg )
1177 print_log( 'ERROR' )
1178 return
1179 raise TypeError(msg)
1180 if isinstance(scan, scantable):
1181 if self._data is not None:
1182 if scan != self._data:
1183 self._data = scan
1184 # reset
1185 self._reset()
1186 else:
1187 self._data = scan
1188 self._reset()
1189 # ranges become invalid when abcissa changes?
1190 #if self._abcunit and self._abcunit != self._data.get_unit():
1191 # self._minmaxx = None
1192 # self._minmaxy = None
1193 # self._abcunit = self._data.get_unit()
1194 # self._datamask = None
1195
1196 # Adjust subplot layouts
1197 if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
1198 lef, bot, rig, top, wsp, hsp = self._panellayout
1199 self._plotter.figure.subplots_adjust(
1200 left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
1201 if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.disable_button()
1202 self._plottp(self._data)
1203 if self._minmaxy is not None:
1204 self._plotter.set_limits(ylim=self._minmaxy)
1205 self._plotter.release()
1206 self._plotter.tidy()
1207 self._plotter.show(hardrefresh=False)
1208 print_log()
1209 return
1210
1211 def _plottp(self,scan):
1212 """
1213 private method for plotting total power data
1214 """
1215 from numpy import ma, array, arange, logical_not
1216 r=0
1217 nr = scan.nrow()
1218 a0,b0 = -1,-1
1219 allxlim = []
1220 allylim = []
1221 y=[]
1222 self._plotter.set_panels()
1223 self._plotter.palette(0)
1224 #title
1225 #xlab = self._abcissa and self._abcissa[panelcount] \
1226 # or scan._getabcissalabel()
1227 #ylab = self._ordinate and self._ordinate[panelcount] \
1228 # or scan._get_ordinate_label()
1229 xlab = self._abcissa or 'row number' #or Time
1230 ylab = self._ordinate or scan._get_ordinate_label()
1231 self._plotter.set_axes('xlabel',xlab)
1232 self._plotter.set_axes('ylabel',ylab)
1233 lbl = self._get_label(scan, r, 's', self._title)
1234 if isinstance(lbl, list) or isinstance(lbl, tuple):
1235 # if 0 <= panelcount < len(lbl):
1236 # lbl = lbl[panelcount]
1237 # else:
1238 # get default label
1239 lbl = self._get_label(scan, r, self._panelling, None)
1240 self._plotter.set_axes('title',lbl)
1241 y=array(scan._get_column(scan._getspectrum,-1))
1242 m = array(scan._get_column(scan._getmask,-1))
1243 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1244 x = arange(len(y))
1245 # try to handle spectral data somewhat...
1246 l,m = y.shape
1247 if m > 1:
1248 y=y.mean(axis=1)
1249 plotit = self._plotter.plot
1250 llbl = self._get_label(scan, r, self._stacking, None)
1251 self._plotter.set_line(label=llbl)
1252 if len(x) > 0:
1253 plotit(x,y)
1254
1255
1256 # forwards to matplotlib.Figure.text
1257 def figtext(self, *args, **kwargs):
1258 """
1259 Add text to figure at location x,y (relative 0-1 coords).
1260 This method forwards *args and **kwargs to a Matplotlib method,
1261 matplotlib.Figure.text.
1262 See the method help for detailed information.
1263 """
1264 self._plotter.text(*args, **kwargs)
1265 # end matplotlib.Figure.text forwarding function
1266
1267
1268 # printing header information
1269 def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
1270 """
1271 print data (scantable) header on the plot and/or logger.
1272 Parameters:
1273 plot: whether or not print header info on the plot.
1274 fontsize: header font size (valid only plot=True)
1275 autoscale: whether or not autoscale the plot (valid only plot=True)
1276 logger: whether or not print header info on the logger.
1277 selstr: additional selection string (not verified)
1278 extrastr: additional string to print (not verified)
1279 """
1280 if not plot and not logger: return
1281 if not self._data: raise RuntimeError("No scantable has been set yet.")
1282 # Now header will be printed on plot and/or logger.
1283 # Get header information and format it.
1284 ssum=self._data.__str__()
1285 # Print Observation header to the upper-left corner of plot
1286 if plot:
1287 headstr=[ssum[ssum.find('Observer:'):ssum.find('Flux Unit:')]]
1288 headstr.append(ssum[ssum.find('Beams:'):ssum.find('Observer:')]
1289 +ssum[ssum.find('Rest Freqs:'):ssum.find('Abcissa:')])
1290 if extrastr != '': headstr[0]=extrastr+'\n'+headstr[0]
1291 #headstr[1]='Data File: '+(filestr or 'unknown')+'\n'+headstr[1]
1292 ssel='***Selections***\n'+(selstr+self._data.get_selection().__str__() or 'none')
1293 headstr.append(ssel)
1294 nstcol=len(headstr)
1295
1296 self._plotter.hold()
1297 for i in range(nstcol):
1298 self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1299 headstr[i],
1300 horizontalalignment='left',
1301 verticalalignment='top',
1302 fontsize=fontsize)
1303 import time
1304 self._plotter.figure.text(0.99,0.0,
1305 time.strftime("%a %d %b %Y %H:%M:%S %Z"),
1306 horizontalalignment='right',
1307 verticalalignment='bottom',fontsize=8)
1308 self._plotter.release()
1309 del headstr, ssel
1310 if logger:
1311 asaplog.push("----------------\n Plot Summary\n----------------")
1312 asaplog.push(extrastr)
1313 asaplog.push(ssum[ssum.find('Beams:'):])
1314 print_log()
1315 del ssum
1316
1317
Note: See TracBrowser for help on using the repository browser.