source: trunk/python/asapplotter.py@ 1840

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

Refactoring of init.py. Moved functionality into separate modules. Some minor fixes to make unit test work under 'standard asap'.

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