source: trunk/python/asapplotter.py@ 1858

Last change on this file since 1858 was 1858, checked in by Malte Marquarding, 15 years ago

Adde more API documentation

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