source: trunk/python/asapplotter.py@ 2604

Last change on this file since 2604 was 2604, checked in by Kana Sugimoto, 12 years ago

New Development: No

JIRA Issue: No (minor improvements)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: unit tests of sdplot

Put in Release Notes: No

Module(s): asapplotter, sdplot (CASA)

Description:

Eliminated duplication of codes.
A minor bug fix to asapplotter.plotgrid.
Initial attempt to add destructor to asapplotter class.


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.7 KB
Line 
1from asap.parameters import rcParams
2from asap.selector import selector
3from asap.scantable import scantable
4from asap.logging import asaplog, asaplog_post_dec
5import matplotlib.axes
6from matplotlib.font_manager import FontProperties
7from matplotlib.text import Text
8from matplotlib import _pylab_helpers
9
10import re
11
12def new_asaplot(visible=None,**kwargs):
13 """
14 Returns a new asaplot instance based on the backend settings.
15 """
16 if visible == None:
17 visible = rcParams['plotter.gui']
18
19 backend=matplotlib.get_backend()
20 if not visible:
21 from asap.asaplot import asaplot
22 elif backend == 'TkAgg':
23 from asap.asaplotgui import asaplotgui as asaplot
24 elif backend == 'Qt4Agg':
25 from asap.asaplotgui_qt4 import asaplotgui as asaplot
26 elif backend == 'GTkAgg':
27 from asap.asaplotgui_gtk import asaplotgui as asaplot
28 else:
29 from asap.asaplot import asaplot
30 return asaplot(**kwargs)
31
32class asapplotter:
33 """
34 The ASAP plotter.
35 By default the plotter is set up to plot polarisations
36 'colour stacked' and scantables across panels.
37
38 .. note::
39
40 Currenly it only plots 'spectra' not Tsys or
41 other variables.
42
43 """
44 def __init__(self, visible=None , **kwargs):
45 self._visible = rcParams['plotter.gui']
46 if visible is not None:
47 self._visible = visible
48 self._plotter = None
49 self._inikwg = kwargs
50
51 self._panelling = None
52 self._stacking = None
53 self.set_panelling()
54 self.set_stacking()
55 self._rows = None
56 self._cols = None
57 self._minmaxx = None
58 self._minmaxy = None
59 self._datamask = None
60 self._data = None
61 self._lmap = None
62 self._title = None
63 self._ordinate = None
64 self._abcissa = None
65 self._abcunit = None
66 self._usermask = []
67 self._maskselection = None
68 self._selection = selector()
69 self._hist = rcParams['plotter.histogram']
70 self._fp = FontProperties()
71 self._margins = self.set_margin(refresh=False)
72 self._offset = None
73 self._startrow = 0
74 self._ipanel = -1
75 self._panelrows = []
76 self._headtext={'string': None, 'textobj': None}
77 self._colormap = None
78 self._linestyles = None
79 self._legendloc = None
80
81 def __del__(self):
82 print "Destructor of sd.plotter"
83 del self._data
84 if self._plotter: self._plotter.quit()
85
86 def _translate(self, instr):
87 keys = "s b i p t r".split()
88 if isinstance(instr, str):
89 for key in keys:
90 if instr.lower().startswith(key):
91 return key
92 return None
93
94 @asaplog_post_dec
95 def _reload_plotter(self):
96 if self._plotter is not None:
97 #if not self._plotter.is_dead:
98 # # clear lines and axes
99 # try:
100 # self._plotter.clear()
101 # except: # Can't remove when already killed.
102 # pass
103 if self.casabar_exists():
104 del self._plotter.figmgr.casabar
105 self._plotter.quit()
106 del self._plotter
107 asaplog.push('Loading new plotter')
108 self._plotter = new_asaplot(self._visible,**self._inikwg)
109 self._plotter.figmgr.casabar=self._new_custombar()
110 # just to make sure they're set
111 self._plotter.palette(color=0,colormap=self._colormap,
112 linestyle=0,linestyles=self._linestyles)
113 self._plotter.legend(self._legendloc)
114
115 def _new_custombar(self):
116 backend=matplotlib.get_backend()
117 if not self._visible:
118 return None
119 elif backend == "TkAgg":
120 from asap.customgui_tkagg import CustomToolbarTkAgg
121 return CustomToolbarTkAgg(self)
122 elif backend == "Qt4Agg":
123 from asap.customgui_qt4agg import CustomToolbarQT4Agg
124 return CustomToolbarQT4Agg(self)
125 return None
126
127 def casabar_exists(self):
128 if not hasattr(self._plotter.figmgr,'casabar'):
129 return False
130 elif self._plotter.figmgr.casabar:
131 return True
132 return False
133
134 def _assert_plotter(self,action="status",errmsg=None):
135 """
136 Check plot window status. Returns True if plot window is alive.
137 Parameters
138 action: An action to take if the plotter window is not alive.
139 ['status'|'reload'|'halt']
140 The action 'status' simply returns False if asaplot
141 is not alive. When action='reload', plot window is
142 reloaded and the method returns True. Finally, an
143 error is raised when action='halt'.
144 errmsg: An error (warning) message to send to the logger,
145 when plot window is not alive.
146 """
147 isAlive = (self._plotter is not None) and self._plotter._alive()
148 # More tests
149 #if isAlive:
150 # if self._plotter.figmgr:
151 # figmgr = self._plotter.figmgr
152 # figid = figmgr.num
153 # # Make sure figid=0 is what asapplotter expects.
154 # # It might be already destroied/overridden by matplotlib
155 # # commands or other plotting methods using asaplot.
156 # isAlive = _pylab_helpers.Gcf.has_fignum(figid) and \
157 # (figmgr == \
158 # _pylab_helpers.Gcf.get_fig_manager(figid))
159 # else:
160 # isAlive = False
161
162 if isAlive:
163 return True
164 # Plotter is not alive.
165 haltmsg = "Plotter window has not yet been loaded or is closed."
166 if type(errmsg)==str and len(errmsg) > 0:
167 haltmsg = errmsg
168
169 if action.upper().startswith("R"):
170 # reload plotter
171 self._reload_plotter()
172 return True
173 elif action.upper().startswith("H"):
174 # halt
175 asaplog.push(haltmsg)
176 asaplog.post("ERROR")
177 raise RuntimeError(haltmsg)
178 else:
179 if errmsg:
180 asaplog.push(errmsg)
181 asaplog.post("WARN")
182 return False
183
184
185 @asaplog_post_dec
186 def plot(self, scan=None):
187 """
188 Plot a scantable.
189 Parameters:
190 scan: a scantable
191 Note:
192 If a scantable was specified in a previous call
193 to plot, no argument has to be given to 'replot'
194 NO checking is done that the abcissas of the scantable
195 are consistent e.g. all 'channel' or all 'velocity' etc.
196 """
197 if not self._data and not scan:
198 msg = "Input is not a scantable"
199 raise TypeError(msg)
200 self._startrow = 0
201 self._ipanel = -1
202 self._reset_header()
203 self._panelrows = []
204
205 self._assert_plotter(action="reload")
206 if self.casabar_exists():
207 self._plotter.figmgr.casabar.set_pagecounter(1)
208
209 self._plotter.hold()
210 #self._plotter.clear()
211 if scan:
212 self.set_data(scan, refresh=False)
213 self._plotter.palette(color=0,colormap=self._colormap,
214 linestyle=0,linestyles=self._linestyles)
215 self._plotter.legend(self._legendloc)
216
217 self._plot(self._data)
218 if self._minmaxy is not None:
219 self._plotter.set_limits(ylim=self._minmaxy)
220 if self.casabar_exists(): self._plotter.figmgr.casabar.enable_button()
221 self._plotter.release()
222 self._plotter.tidy()
223 self._plotter.show(hardrefresh=False)
224 return
225
226 def gca(self):
227 errmsg = "No axis to retun. Need to plot first."
228 if not self._assert_plotter(action="status",errmsg=errmsg):
229 return None
230 return self._plotter.figure.gca()
231
232 def refresh(self):
233 """Do a soft refresh"""
234 errmsg = "No figure to re-plot. Need to plot first."
235 self._assert_plotter(action="halt",errmsg=errmsg)
236
237 self._plotter.figure.show()
238
239 def create_mask(self, nwin=1, panel=0, color=None):
240 """
241 Interactively define a mask. It retruns a mask that is equivalent to
242 the one created manually with scantable.create_mask.
243 Parameters:
244 nwin: The number of mask windows to create interactively
245 default is 1.
246 panel: Which panel to use for mask selection. This is useful
247 if different IFs are spread over panels (default 0)
248 """
249 ## this method relies on already plotted figure
250 if not self._assert_plotter(action="status") or (self._data is None):
251 msg = "Cannot create mask interactively on plot. Can only create mask after plotting."
252 asaplog.push( msg )
253 asaplog.post( "ERROR" )
254 return []
255 outmask = []
256 self._plotter.subplot(panel)
257 xmin, xmax = self._plotter.axes.get_xlim()
258 marg = 0.05*(xmax-xmin)
259 self._plotter.axes.set_xlim(xmin-marg, xmax+marg)
260 self.refresh()
261
262 def cleanup(lines=False, texts=False, refresh=False):
263 if lines:
264 del self._plotter.axes.lines[-1]
265 if texts:
266 del self._plotter.axes.texts[-1]
267 if refresh:
268 self.refresh()
269
270 for w in xrange(nwin):
271 wpos = []
272 self.text(0.05,1.0, "Add start boundary",
273 coords="relative", fontsize=10)
274 point = self._plotter.get_point()
275 cleanup(texts=True)
276 if point is None:
277 continue
278 wpos.append(point[0])
279 self.axvline(wpos[0], color=color)
280 self.text(0.05,1.0, "Add end boundary", coords="relative", fontsize=10)
281 point = self._plotter.get_point()
282 cleanup(texts=True, lines=True)
283 if point is None:
284 self.refresh()
285 continue
286 wpos.append(point[0])
287 self.axvspan(wpos[0], wpos[1], alpha=0.1,
288 edgecolor=color, facecolor=color)
289 ymin, ymax = self._plotter.axes.get_ylim()
290 outmask.append(wpos)
291
292 self._plotter.axes.set_xlim(xmin, xmax)
293 self.refresh()
294 if len(outmask) > 0:
295 return self._data.create_mask(*outmask)
296 return []
297
298 # forwards to matplotlib axes
299 def text(self, *args, **kwargs):
300 self._assert_plotter(action="reload")
301 if kwargs.has_key("interactive"):
302 if kwargs.pop("interactive"):
303 pos = self._plotter.get_point()
304 args = tuple(pos)+args
305 self._axes_callback("text", *args, **kwargs)
306
307 text.__doc__ = matplotlib.axes.Axes.text.__doc__
308
309 def arrow(self, *args, **kwargs):
310 self._assert_plotter(action="reload")
311 if kwargs.has_key("interactive"):
312 if kwargs.pop("interactive"):
313 pos = self._plotter.get_region()
314 dpos = (pos[0][0], pos[0][1],
315 pos[1][0]-pos[0][0],
316 pos[1][1] - pos[0][1])
317 args = dpos + args
318 self._axes_callback("arrow", *args, **kwargs)
319
320 arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
321
322 def annotate(self, text, xy=None, xytext=None, **kwargs):
323 self._assert_plotter(action="reload")
324 if kwargs.has_key("interactive"):
325 if kwargs.pop("interactive"):
326 xy = self._plotter.get_point()
327 xytext = self._plotter.get_point()
328 if not kwargs.has_key("arrowprops"):
329 kwargs["arrowprops"] = dict(arrowstyle="->")
330 self._axes_callback("annotate", text, xy, xytext, **kwargs)
331
332 annotate.__doc__ = matplotlib.axes.Axes.annotate.__doc__
333
334 def axvline(self, *args, **kwargs):
335 self._assert_plotter(action="reload")
336 if kwargs.has_key("interactive"):
337 if kwargs.pop("interactive"):
338 pos = self._plotter.get_point()
339 args = (pos[0],)+args
340 self._axes_callback("axvline", *args, **kwargs)
341
342 axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
343
344 def axhline(self, *args, **kwargs):
345 self._assert_plotter(action="reload")
346 if kwargs.has_key("interactive"):
347 if kwargs.pop("interactive"):
348 pos = self._plotter.get_point()
349 args = (pos[1],)+args
350 self._axes_callback("axhline", *args, **kwargs)
351
352 axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
353
354 def axvspan(self, *args, **kwargs):
355 self._assert_plotter(action="reload")
356 if kwargs.has_key("interactive"):
357 if kwargs.pop("interactive"):
358 pos = self._plotter.get_region()
359 dpos = (pos[0][0], pos[1][0])
360 args = dpos + args
361 self._axes_callback("axvspan", *args, **kwargs)
362 # hack to preventy mpl from redrawing the patch
363 # it seem to convert the patch into lines on every draw.
364 # This doesn't happen in a test script???
365 #del self._plotter.axes.patches[-1]
366
367 axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
368
369 def axhspan(self, *args, **kwargs):
370 self._assert_plotter(action="reload")
371 if kwargs.has_key("interactive"):
372 if kwargs.pop("interactive"):
373 pos = self._plotter.get_region()
374 dpos = (pos[0][1], pos[1][1])
375 args = dpos + args
376 self._axes_callback("axhspan", *args, **kwargs)
377 # hack to preventy mpl from redrawing the patch
378 # it seem to convert the patch into lines on every draw.
379 # This doesn't happen in a test script???
380 #del self._plotter.axes.patches[-1]
381
382 axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
383
384 def _axes_callback(self, axesfunc, *args, **kwargs):
385 self._assert_plotter(action="reload")
386 panel = 0
387 if kwargs.has_key("panel"):
388 panel = kwargs.pop("panel")
389 coords = None
390 if kwargs.has_key("coords"):
391 coords = kwargs.pop("coords")
392 if coords.lower() == 'world':
393 kwargs["transform"] = self._plotter.axes.transData
394 elif coords.lower() == 'relative':
395 kwargs["transform"] = self._plotter.axes.transAxes
396 self._plotter.subplot(panel)
397 self._plotter.axes.set_autoscale_on(False)
398 getattr(self._plotter.axes, axesfunc)(*args, **kwargs)
399 self._plotter.show(False)
400 self._plotter.axes.set_autoscale_on(True)
401 # end matplotlib.axes fowarding functions
402
403 @asaplog_post_dec
404 def set_data(self, scan, refresh=True):
405 """
406 Set a scantable to plot.
407 Parameters:
408 scan: a scantable
409 refresh: True (default) or False. If True, the plot is
410 replotted based on the new parameter setting(s).
411 Otherwise,the parameter(s) are set without replotting.
412 Note:
413 The user specified masks and data selections will be reset
414 if a new scantable is set. This method should be called before
415 setting data selections (set_selection) and/or masks (set_mask).
416 """
417 from asap import scantable
418 if isinstance(scan, scantable):
419 if (self._data is not None) and (scan != self._data):
420 del self._data
421 msg = "A new scantable is set to the plotter. "\
422 "The masks and data selections are reset."
423 asaplog.push( msg )
424 self._data = scan
425 # reset
426 self._reset()
427 else:
428 msg = "Input is not a scantable"
429 raise TypeError(msg)
430
431 # ranges become invalid when unit changes
432 if self._abcunit and self._abcunit != self._data.get_unit():
433 self._minmaxx = None
434 self._minmaxy = None
435 self._abcunit = self._data.get_unit()
436 self._datamask = None
437 if refresh: self.plot()
438
439 @asaplog_post_dec
440 def set_mode(self, stacking=None, panelling=None, refresh=True):
441 """
442 Set the plots look and feel, i.e. what you want to see on the plot.
443 Parameters:
444 stacking: tell the plotter which variable to plot
445 as line colour overlays (default 'pol')
446 panelling: tell the plotter which variable to plot
447 across multiple panels (default 'scan'
448 refresh: True (default) or False. If True, the plot is
449 replotted based on the new parameter setting(s).
450 Otherwise,the parameter(s) are set without replotting.
451 Note:
452 Valid modes are:
453 'beam' 'Beam' 'b': Beams
454 'if' 'IF' 'i': IFs
455 'pol' 'Pol' 'p': Polarisations
456 'scan' 'Scan' 's': Scans
457 'time' 'Time' 't': Times
458 'row' 'Row' 'r': Rows
459 When either 'stacking' or 'panelling' is set to 'row',
460 the other parameter setting is ignored.
461 """
462 msg = "Invalid mode"
463 if not self.set_panelling(panelling) or \
464 not self.set_stacking(stacking):
465 raise TypeError(msg)
466 #if self._panelling == 'r':
467 # self._stacking = '_r'
468 #if self._stacking == 'r':
469 # self._panelling = '_r'
470 if refresh and self._data: self.plot(self._data)
471 return
472
473 def set_panelling(self, what=None):
474 """Set the 'panelling' mode i.e. which type of spectra should be
475 spread across different panels.
476 """
477
478 mode = what
479 if mode is None:
480 mode = rcParams['plotter.panelling']
481 md = self._translate(mode)
482 if md:
483 self._panelling = md
484 self._title = None
485 #if md == 'r':
486 # self._stacking = '_r'
487 # you need to reset counters for multi page plotting
488 self._reset_counters()
489 return True
490 return False
491
492 def set_layout(self,rows=None,cols=None,refresh=True):
493 """
494 Set the multi-panel layout, i.e. how many rows and columns plots
495 are visible.
496 Parameters:
497 rows: The number of rows of plots
498 cols: The number of columns of plots
499 refresh: True (default) or False. If True, the plot is
500 replotted based on the new parameter setting(s).
501 Otherwise,the parameter(s) are set without replotting.
502 Note:
503 If no argument is given, the potter reverts to its auto-plot
504 behaviour.
505 """
506 self._rows = rows
507 self._cols = cols
508 if refresh and self._data: self.plot(self._data)
509 return
510
511 def set_stacking(self, what=None):
512 """Set the 'stacking' mode i.e. which type of spectra should be
513 overlayed.
514 """
515 mode = what
516 if mode is None:
517 mode = rcParams['plotter.stacking']
518 md = self._translate(mode)
519 if md:
520 self._stacking = md
521 self._lmap = None
522 #if md == 'r':
523 # self._panelling = '_r'
524 # you need to reset counters for multi page plotting
525 self._reset_counters()
526 return True
527 return False
528
529 def _reset_counters(self):
530 self._startrow = 0
531 self._ipanel = -1
532 self._panelrows = []
533
534 def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True, offset=None):
535 """
536 Set the range of interest on the abcissa of the plot
537 Parameters:
538 [x,y]start,[x,y]end: The start and end points of the 'zoom' window
539 refresh: True (default) or False. If True, the plot is
540 replotted based on the new parameter setting(s).
541 Otherwise,the parameter(s) are set without replotting.
542 offset: shift the abcissa by the given amount. The abcissa label will
543 have '(relative)' appended to it.
544 Note:
545 These become non-sensical when the unit changes.
546 use plotter.set_range() without parameters to reset
547
548 """
549 self._offset = offset
550 if xstart is None and xend is None:
551 self._minmaxx = None
552 else:
553 self._minmaxx = [xstart,xend]
554 if ystart is None and yend is None:
555 self._minmaxy = None
556 else:
557 self._minmaxy = [ystart,yend]
558 if refresh and self._data: self.plot(self._data)
559 return
560
561 def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
562 """
563 Specify a mapping for the legend instead of using the default
564 indices:
565 Parameters:
566 mp: a list of 'strings'. This should have the same length
567 as the number of elements on the legend and then maps
568 to the indeces in order. It is possible to uses latex
569 math expression. These have to be enclosed in r'',
570 e.g. r'$x^{2}$'
571 fontsize: The font size of the label (default None)
572 mode: where to display the legend
573 Any other value for loc else disables the legend:
574 0: auto
575 1: upper right
576 2: upper left
577 3: lower left
578 4: lower right
579 5: right
580 6: center left
581 7: center right
582 8: lower center
583 9: upper center
584 10: center
585 refresh: True (default) or False. If True, the plot is
586 replotted based on the new parameter setting(s).
587 Otherwise,the parameter(s) are set without replotting.
588
589 Example:
590 If the data has two IFs/rest frequencies with index 0 and 1
591 for CO and SiO:
592 plotter.set_stacking('i')
593 plotter.set_legend(['CO','SiO'])
594 plotter.plot()
595 plotter.set_legend([r'$^{12}CO$', r'SiO'])
596 """
597 self._lmap = mp
598 #self._plotter.legend(mode)
599 self._legendloc = mode
600 if isinstance(fontsize, int):
601 from matplotlib import rc as rcp
602 rcp('legend', fontsize=fontsize)
603 if refresh and self._data: self.plot(self._data)
604 return
605
606 def set_title(self, title=None, fontsize=None, refresh=True):
607 """
608 Set the title of sub-plots. If multiple sub-plots are plotted,
609 multiple titles have to be specified.
610 Parameters:
611 title: a list of titles of sub-plots.
612 fontsize: a font size of titles (integer)
613 refresh: True (default) or False. If True, the plot is
614 replotted based on the new parameter setting(s).
615 Otherwise,the parameter(s) are set without replotting.
616 Example:
617 # two panels are visible on the plotter
618 plotter.set_title(['First Panel','Second Panel'])
619 """
620 self._title = title
621 if isinstance(fontsize, int):
622 from matplotlib import rc as rcp
623 rcp('axes', titlesize=fontsize)
624 if refresh and self._data: self.plot(self._data)
625 return
626
627 def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
628 """
629 Set the y-axis label of the plot. If multiple panels are plotted,
630 multiple labels have to be specified.
631 Parameters:
632 ordinate: a list of ordinate labels. None (default) let
633 data determine the labels
634 fontsize: a font size of vertical axis labels (integer)
635 refresh: True (default) or False. If True, the plot is
636 replotted based on the new parameter setting(s).
637 Otherwise,the parameter(s) are set without replotting.
638 Example:
639 # two panels are visible on the plotter
640 plotter.set_ordinate(['First Y-Axis','Second Y-Axis'])
641 """
642 self._ordinate = ordinate
643 if isinstance(fontsize, int):
644 from matplotlib import rc as rcp
645 rcp('axes', labelsize=fontsize)
646 rcp('ytick', labelsize=fontsize)
647 if refresh and self._data: self.plot(self._data)
648 return
649
650 def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
651 """
652 Set the x-axis label of the plot. If multiple panels are plotted,
653 multiple labels have to be specified.
654 Parameters:
655 abcissa: a list of abcissa labels. None (default) let
656 data determine the labels
657 fontsize: a font size of horizontal axis labels (integer)
658 refresh: True (default) or False. If True, the plot is
659 replotted based on the new parameter setting(s).
660 Otherwise,the parameter(s) are set without replotting.
661 Example:
662 # two panels are visible on the plotter
663 plotter.set_ordinate(['First X-Axis','Second X-Axis'])
664 """
665 self._abcissa = abcissa
666 if isinstance(fontsize, int):
667 from matplotlib import rc as rcp
668 rcp('axes', labelsize=fontsize)
669 rcp('xtick', labelsize=fontsize)
670 if refresh and self._data: self.plot(self._data)
671 return
672
673 def set_colors(self, colmap, refresh=True):
674 """
675 Set the colours to be used. The plotter will cycle through
676 these colours when lines are overlaid (stacking mode).
677 Parameters:
678 colmap: a list of colour names
679 refresh: True (default) or False. If True, the plot is
680 replotted based on the new parameter setting(s).
681 Otherwise,the parameter(s) are set without replotting.
682 Example:
683 plotter.set_colors('red green blue')
684 # If for example four lines are overlaid e.g I Q U V
685 # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
686 # and 'V' will be 'red' again.
687 """
688 #if isinstance(colmap,str):
689 # colmap = colmap.split()
690 #self._plotter.palette(0, colormap=colmap)
691 self._colormap = colmap
692 if refresh and self._data: self.plot(self._data)
693
694 # alias for english speakers
695 set_colours = set_colors
696
697 def set_histogram(self, hist=True, linewidth=None, refresh=True):
698 """
699 Enable/Disable histogram-like plotting.
700 Parameters:
701 hist: True (default) or False. The fisrt default
702 is taken from the .asaprc setting
703 plotter.histogram
704 linewidth: a line width
705 refresh: True (default) or False. If True, the plot is
706 replotted based on the new parameter setting(s).
707 Otherwise,the parameter(s) are set without replotting.
708 """
709 self._hist = hist
710 if isinstance(linewidth, float) or isinstance(linewidth, int):
711 from matplotlib import rc as rcp
712 rcp('lines', linewidth=linewidth)
713 if refresh and self._data: self.plot(self._data)
714
715 def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
716 """
717 Set the linestyles to be used. The plotter will cycle through
718 these linestyles when lines are overlaid (stacking mode) AND
719 only one color has been set.
720 Parameters:
721 linestyles: a list of linestyles to use.
722 'line', 'dashed', 'dotted', 'dashdot',
723 'dashdotdot' and 'dashdashdot' are
724 possible
725 linewidth: a line width
726 refresh: True (default) or False. If True, the plot is
727 replotted based on the new parameter setting(s).
728 Otherwise,the parameter(s) are set without replotting.
729 Example:
730 plotter.set_colors('black')
731 plotter.set_linestyles('line dashed dotted dashdot')
732 # If for example four lines are overlaid e.g I Q U V
733 # 'I' will be 'solid', 'Q' will be 'dashed',
734 # U will be 'dotted' and 'V' will be 'dashdot'.
735 """
736 #if isinstance(linestyles,str):
737 # linestyles = linestyles.split()
738 #self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
739 self._linestyles = linestyles
740 if isinstance(linewidth, float) or isinstance(linewidth, int):
741 from matplotlib import rc as rcp
742 rcp('lines', linewidth=linewidth)
743 if refresh and self._data: self.plot(self._data)
744
745 def set_font(self, refresh=True,**kwargs):
746 """
747 Set font properties.
748 Parameters:
749 family: one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
750 style: one of 'normal' (or 'roman'), 'italic' or 'oblique'
751 weight: one of 'normal or 'bold'
752 size: the 'general' font size, individual elements can be adjusted
753 seperately
754 refresh: True (default) or False. If True, the plot is
755 replotted based on the new parameter setting(s).
756 Otherwise,the parameter(s) are set without replotting.
757 """
758 from matplotlib import rc as rcp
759 fdict = {}
760 for k,v in kwargs.iteritems():
761 if v:
762 fdict[k] = v
763 self._fp = FontProperties(**fdict)
764 if refresh and self._data: self.plot(self._data)
765
766 def set_margin(self,margin=[],refresh=True):
767 """
768 Set margins between subplots and plot edges.
769 Parameters:
770 margin: a list of margins in figure coordinate (0-1),
771 i.e., fraction of the figure width or height.
772 The order of elements should be:
773 [left, bottom, right, top, horizontal space btw panels,
774 vertical space btw panels].
775 refresh: True (default) or False. If True, the plot is
776 replotted based on the new parameter setting(s).
777 Otherwise,the parameter(s) are set without replotting.
778 Note
779 * When margin is not specified, the values are reset to the defaults
780 of matplotlib.
781 * If any element is set to be None, the current value is adopted.
782 """
783 if margin == []: self._margins=self._reset_margin()
784 else:
785 self._margins=[None]*6
786 self._margins[0:len(margin)]=margin
787 #print "panel margin set to ",self._margins
788 if refresh and self._data: self.plot(self._data)
789
790 def _reset_margin(self):
791 ks=map(lambda x: 'figure.subplot.'+x,
792 ['left','bottom','right','top','hspace','wspace'])
793 return map(matplotlib.rcParams.get,ks)
794
795 def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
796 location=None):
797 """
798 Plot a line catalog.
799 Parameters:
800 linecat: the linecatalog to plot
801 doppler: the velocity shift to apply to the frequencies
802 deltachan: the number of channels to include each side of the
803 line to determine a local maximum/minimum
804 rotate: the rotation (in degrees) for the text label (default 90.0)
805 location: the location of the line annotation from the 'top',
806 'bottom' or alternate (None - the default)
807 Notes:
808 If the spectrum is flagged no line will be drawn in that location.
809 """
810 errmsg = "Cannot plot spectral lines. Need to plot scantable first."
811 self._assert_plotter(action="halt",errmsg=errmsg)
812 if not self._data:
813 raise RuntimeError("No scantable has been plotted yet.")
814 from asap._asap import linecatalog
815 if not isinstance(linecat, linecatalog):
816 raise ValueError("'linecat' isn't of type linecatalog.")
817 if not self._data.get_unit().endswith("Hz"):
818 raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
819 from numpy import ma
820 for j in range(len(self._plotter.subplots)):
821 self._plotter.subplot(j)
822 lims = self._plotter.axes.get_xlim()
823 for row in range(linecat.nrow()):
824 # get_frequency returns MHz
825 base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
826 restf = linecat.get_frequency(row)/base[self._data.get_unit()]
827 c = 299792.458
828 freq = restf*(1.0-doppler/c)
829 if lims[0] < freq < lims[1]:
830 if location is None:
831 loc = 'bottom'
832 if row%2: loc='top'
833 else: loc = location
834 maxys = []
835 for line in self._plotter.axes.lines:
836 v = line._x
837 asc = v[0] < v[-1]
838
839 idx = None
840 if not asc:
841 if v[len(v)-1] <= freq <= v[0]:
842 i = len(v)-1
843 while i>=0 and v[i] < freq:
844 idx = i
845 i-=1
846 else:
847 if v[0] <= freq <= v[len(v)-1]:
848 i = 0
849 while i<len(v) and v[i] < freq:
850 idx = i
851 i+=1
852 if idx is not None:
853 lower = idx - deltachan
854 upper = idx + deltachan
855 if lower < 0: lower = 0
856 if upper > len(v): upper = len(v)
857 s = slice(lower, upper)
858 y = line._y[s]
859 maxy = ma.maximum(y)
860 if isinstance( maxy, float):
861 maxys.append(maxy)
862 if len(maxys):
863 peak = max(maxys)
864 if peak > self._plotter.axes.get_ylim()[1]:
865 loc = 'bottom'
866 else:
867 continue
868 self._plotter.vline_with_label(freq, peak,
869 linecat.get_name(row),
870 location=loc, rotate=rotate)
871 self._plotter.show(hardrefresh=False)
872
873
874 def save(self, filename=None, orientation=None, dpi=None):
875 """
876 Save the plot to a file. The known formats are 'png', 'ps', 'eps'.
877 Parameters:
878 filename: The name of the output file. This is optional
879 and autodetects the image format from the file
880 suffix. If non filename is specified a file
881 called 'yyyymmdd_hhmmss.png' is created in the
882 current directory.
883 orientation: optional parameter for postscript only (not eps).
884 'landscape', 'portrait' or None (default) are valid.
885 If None is choosen for 'ps' output, the plot is
886 automatically oriented to fill the page.
887 dpi: The dpi of the output non-ps plot
888 """
889 errmsg = "Cannot save figure. Need to plot first."
890 self._assert_plotter(action="halt",errmsg=errmsg)
891
892 self._plotter.save(filename,orientation,dpi)
893 return
894
895 @asaplog_post_dec
896 def set_mask(self, mask=None, selection=None, refresh=True):
897 """
898 Set a plotting mask for a specific polarization.
899 This is useful for masking out 'noise' Pangle outside a source.
900 Parameters:
901 mask: a mask from scantable.create_mask
902 selection: the spectra to apply the mask to.
903 refresh: True (default) or False. If True, the plot is
904 replotted based on the new parameter setting(s).
905 Otherwise,the parameter(s) are set without replotting.
906 Example:
907 select = selector()
908 select.setpolstrings('Pangle')
909 plotter.set_mask(mymask, select)
910 """
911 if not self._data:
912 msg = "Can only set mask after a first call to plot()"
913 raise RuntimeError(msg)
914 if len(mask):
915 if isinstance(mask, list) or isinstance(mask, tuple):
916 self._usermask = array(mask)
917 else:
918 self._usermask = mask
919 if mask is None and selection is None:
920 self._usermask = []
921 self._maskselection = None
922 if isinstance(selection, selector):
923 self._maskselection = {'b': selection.get_beams(),
924 's': selection.get_scans(),
925 'i': selection.get_ifs(),
926 'p': selection.get_pols(),
927 't': [] }
928 else:
929 self._maskselection = None
930 if refresh: self.plot(self._data)
931
932 def _slice_indeces(self, data):
933 mn = self._minmaxx[0]
934 mx = self._minmaxx[1]
935 asc = data[0] < data[-1]
936 start=0
937 end = len(data)-1
938 inc = 1
939 if not asc:
940 start = len(data)-1
941 end = 0
942 inc = -1
943 # find min index
944 #while start > 0 and data[start] < mn:
945 # start+= inc
946 minind=start
947 for ind in xrange(start,end+inc,inc):
948 if data[ind] > mn: break
949 minind=ind
950 # find max index
951 #while end > 0 and data[end] > mx:
952 # end-=inc
953 #if end > 0: end +=1
954 maxind=end
955 for ind in xrange(end,start-inc,-inc):
956 if data[ind] < mx: break
957 maxind=ind
958 start=minind
959 end=maxind
960 if start > end:
961 return end,start+1
962 elif start < end:
963 return start,end+1
964 else:
965 return start,end
966
967 def _reset(self):
968 self._usermask = []
969 self._usermaskspectra = None
970 self._offset = None
971 self.set_selection(None, False)
972 self._reset_header()
973
974 def _reset_header(self):
975 self._headtext={'string': None, 'textobj': None}
976
977 def _plot(self, scan):
978 savesel = scan.get_selection()
979 sel = savesel + self._selection
980 order = self._get_sortstring([self._panelling,self._stacking])
981 if order:
982 sel.set_order(order)
983 scan.set_selection(sel)
984 d = {'b': scan.getbeam, 's': scan.getscan,
985 'i': scan.getif, 'p': scan.getpol, 't': scan.get_time,
986 'r': int}#, '_r': int}
987
988 polmodes = dict(zip(self._selection.get_pols(),
989 self._selection.get_poltypes()))
990 # this returns either a tuple of numbers or a length (ncycles)
991 # convert this into lengths
992 n0,nstack0 = self._get_selected_n(scan)
993 if isinstance(n0, int): n = n0
994 else: n = len(n0)
995 if isinstance(nstack0, int): nstack = nstack0
996 else: nstack = len(nstack0)
997 # In case of row stacking
998 rowstack = False
999 titlemode = self._panelling
1000 if self._stacking == "r" and self._panelling != "r":
1001 rowstack = True
1002 titlemode = '_r'
1003 nptot = n
1004 maxpanel, maxstack = 16,16
1005 if nstack > maxstack:
1006 msg ="Scan to be overlayed contains more than %d selections.\n" \
1007 "Selecting first %d selections..." % (maxstack, maxstack)
1008 asaplog.push(msg)
1009 asaplog.post('WARN')
1010 nstack = min(nstack,maxstack)
1011 #n = min(n-self._ipanel-1,maxpanel)
1012 n = n-self._ipanel-1
1013
1014 ganged = False
1015 if n > 1:
1016 ganged = rcParams['plotter.ganged']
1017 if self._panelling == 'i':
1018 ganged = False
1019 if self._rows and self._cols:
1020 n = min(n,self._rows*self._cols)
1021 self._plotter.set_panels(rows=self._rows,cols=self._cols,
1022 nplots=n,margin=self._margins,ganged=ganged)
1023 else:
1024 n = min(n,maxpanel)
1025 self._plotter.set_panels(rows=n,cols=0,nplots=n,margin=self._margins,ganged=ganged)
1026 else:
1027 self._plotter.set_panels(margin=self._margins)
1028 #r = 0
1029 r = self._startrow
1030 nr = scan.nrow()
1031 a0,b0 = -1,-1
1032 allxlim = []
1033 allylim = []
1034 #newpanel=True
1035 newpanel=False
1036 panelcount,stackcount = 0,0
1037 # If this is not the first page
1038 if r > 0:
1039 # panelling value of the prev page
1040 a0 = d[self._panelling](r-1)
1041 # set the initial stackcount large not to plot
1042 # the start row automatically
1043 stackcount = nstack
1044
1045 while r < nr:
1046 a = d[self._panelling](r)
1047 b = d[self._stacking](r)
1048 if a > a0 and panelcount < n:
1049 if n > 1:
1050 self._plotter.subplot(panelcount)
1051 self._plotter.palette(0)
1052 #title
1053 xlab = self._abcissa and self._abcissa[panelcount] \
1054 or scan._getabcissalabel()
1055 if self._offset and not self._abcissa:
1056 xlab += " (relative)"
1057 ylab = self._ordinate and self._ordinate[panelcount] \
1058 or scan._get_ordinate_label()
1059 self._plotter.set_axes('xlabel', xlab)
1060 self._plotter.set_axes('ylabel', ylab)
1061 #lbl = self._get_label(scan, r, self._panelling, self._title)
1062 lbl = self._get_label(scan, r, titlemode, self._title)
1063 if isinstance(lbl, list) or isinstance(lbl, tuple):
1064 if 0 <= panelcount < len(lbl):
1065 lbl = lbl[panelcount]
1066 else:
1067 # get default label
1068 #lbl = self._get_label(scan, r, self._panelling, None)
1069 lbl = self._get_label(scan, r, titlemode, None)
1070 self._plotter.set_axes('title',lbl)
1071 newpanel = True
1072 stackcount = 0
1073 panelcount += 1
1074 # save the start row to plot this panel for future revisit.
1075 if self._panelling != 'r' and \
1076 len(self._panelrows) < self._ipanel+1+panelcount:
1077 self._panelrows += [r]
1078
1079 #if (b > b0 or newpanel) and stackcount < nstack:
1080 if stackcount < nstack and (newpanel or rowstack or (a == a0 and b > b0)):
1081 y = []
1082 if len(polmodes):
1083 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
1084 else:
1085 y = scan._getspectrum(r)
1086 # flag application
1087 mr = scan._getflagrow(r)
1088 from numpy import ma, array
1089 if mr:
1090 y = ma.masked_array(y,mask=mr)
1091 else:
1092 m = scan._getmask(r)
1093 from numpy import logical_not, logical_and
1094 if self._maskselection and len(self._usermask) == len(m):
1095 if d[self._stacking](r) in self._maskselection[self._stacking]:
1096 m = logical_and(m, self._usermask)
1097 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1098
1099 x = array(scan._getabcissa(r))
1100 if self._offset:
1101 x += self._offset
1102 if self._minmaxx is not None:
1103 s,e = self._slice_indeces(x)
1104 x = x[s:e]
1105 y = y[s:e]
1106 if len(x) > 1024 and rcParams['plotter.decimate']:
1107 fac = len(x)/1024
1108 x = x[::fac]
1109 y = y[::fac]
1110 llbl = self._get_label(scan, r, self._stacking, self._lmap)
1111 if isinstance(llbl, list) or isinstance(llbl, tuple):
1112 if 0 <= stackcount < len(llbl):
1113 # use user label
1114 llbl = llbl[stackcount]
1115 else:
1116 # get default label
1117 llbl = self._get_label(scan, r, self._stacking, None)
1118 self._plotter.set_line(label=llbl)
1119 plotit = self._plotter.plot
1120 if self._hist: plotit = self._plotter.hist
1121 if len(x) > 0 and not mr:
1122 plotit(x,y)
1123 xlim= self._minmaxx or [min(x),max(x)]
1124 allxlim += xlim
1125 ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
1126 allylim += ylim
1127 else:
1128 xlim = self._minmaxx or []
1129 allxlim += xlim
1130 ylim= self._minmaxy or []
1131 allylim += ylim
1132 stackcount += 1
1133 a0=a
1134 b0=b
1135 # last in colour stack -> autoscale x
1136 if stackcount == nstack and len(allxlim) > 0:
1137 allxlim.sort()
1138 self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
1139 if ganged:
1140 allxlim = [allxlim[0],allxlim[-1]]
1141 else:
1142 # clear
1143 allxlim =[]
1144
1145 newpanel = False
1146 #a0=a
1147 #b0=b
1148 # ignore following rows
1149 if (panelcount == n and stackcount == nstack) or (r == nr-1):
1150 # last panel -> autoscale y if ganged
1151 #if rcParams['plotter.ganged'] and len(allylim) > 0:
1152 if ganged and len(allylim) > 0:
1153 allylim.sort()
1154 self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
1155 break
1156 r+=1 # next row
1157
1158 # save the current counter for multi-page plotting
1159 self._startrow = r+1
1160 self._ipanel += panelcount
1161 if self.casabar_exists():
1162 if self._ipanel >= nptot-1:
1163 self._plotter.figmgr.casabar.disable_next()
1164 else:
1165 self._plotter.figmgr.casabar.enable_next()
1166 if self._ipanel + 1 - panelcount > 0:
1167 self._plotter.figmgr.casabar.enable_prev()
1168 else:
1169 self._plotter.figmgr.casabar.disable_prev()
1170
1171 #reset the selector to the scantable's original
1172 scan.set_selection(savesel)
1173
1174 #temporary switch-off for older matplotlib
1175 #if self._fp is not None:
1176 if self._fp is not None and getattr(self._plotter.figure,'findobj',False):
1177 for o in self._plotter.figure.findobj(Text):
1178 o.set_fontproperties(self._fp)
1179
1180 def _get_sortstring(self, lorders):
1181 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
1182 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME', 'r':None, '_r':None }
1183 if not (type(lorders) == list) and not (type(lorders) == tuple):
1184 return None
1185 if len(lorders) > 0:
1186 lsorts = []
1187 for order in lorders:
1188 if order == "r":
1189 # don't sort if row panelling/stacking
1190 return None
1191 ssort = d0[order]
1192 if ssort:
1193 lsorts.append(ssort)
1194 return lsorts
1195 return None
1196
1197 def set_selection(self, selection=None, refresh=True, **kw):
1198 """
1199 Parameters:
1200 selection: a selector object (default unset the selection)
1201 refresh: True (default) or False. If True, the plot is
1202 replotted based on the new parameter setting(s).
1203 Otherwise,the parameter(s) are set without replotting.
1204 """
1205 if selection is None:
1206 # reset
1207 if len(kw) == 0:
1208 self._selection = selector()
1209 else:
1210 # try keywords
1211 for k in kw:
1212 if k not in selector.fields:
1213 raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
1214 self._selection = selector(**kw)
1215 elif isinstance(selection, selector):
1216 self._selection = selection
1217 else:
1218 raise TypeError("'selection' is not of type selector")
1219
1220 order = self._get_sortstring([self._panelling,self._stacking])
1221 if order:
1222 self._selection.set_order(order)
1223 if refresh and self._data: self.plot(self._data)
1224
1225 def _get_selected_n(self, scan):
1226 d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
1227 'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle,
1228 'r': scan.nrow}#, '_r': False}
1229 d2 = { 'b': self._selection.get_beams(),
1230 's': self._selection.get_scans(),
1231 'i': self._selection.get_ifs(),
1232 'p': self._selection.get_pols(),
1233 't': self._selection.get_cycles(),
1234 'r': False}#, '_r': 1}
1235 n = d2[self._panelling] or d1[self._panelling]()
1236 nstack = d2[self._stacking] or d1[self._stacking]()
1237 # handle row panelling/stacking
1238 if self._panelling == 'r':
1239 nstack = 1
1240 elif self._stacking == 'r':
1241 n = 1
1242 return n,nstack
1243
1244 def _get_label(self, scan, row, mode, userlabel=None):
1245 if isinstance(userlabel, list) and len(userlabel) == 0:
1246 userlabel = " "
1247 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
1248 if len(pms):
1249 poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
1250 else:
1251 poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
1252 d = {'b': "Beam "+str(scan.getbeam(row)),
1253 #'s': scan._getsourcename(row),
1254 's': "Scan "+str(scan.getscan(row))+\
1255 " ("+str(scan._getsourcename(row))+")",
1256 'i': "IF"+str(scan.getif(row)),
1257 'p': poleval,
1258 't': str(scan.get_time(row)),
1259 'r': "row "+str(row),
1260 #'_r': str(scan.get_time(row))+",\nIF"+str(scan.getif(row))+", "+poleval+", Beam"+str(scan.getbeam(row)) }
1261 '_r': "" }
1262 return userlabel or d[mode]
1263
1264 def plotazel(self, scan=None, outfile=None):
1265 """
1266 plot azimuth and elevation versus time of a scantable
1267 """
1268 visible = rcParams['plotter.gui']
1269 from matplotlib import pylab as PL
1270 from matplotlib.dates import DateFormatter
1271 from pytz import timezone
1272 from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
1273 from matplotlib.ticker import MultipleLocator
1274 from numpy import array, pi
1275 if not visible or not self._visible:
1276 PL.ioff()
1277 from matplotlib.backends.backend_agg import FigureCanvasAgg
1278 PL.gcf().canvas.switch_backends(FigureCanvasAgg)
1279 self._data = scan
1280 dates = self._data.get_time(asdatetime=True)
1281 t = PL.date2num(dates)
1282 tz = timezone('UTC')
1283 PL.cla()
1284 PL.ioff()
1285 PL.clf()
1286 # Adjust subplot margins
1287 if not self._margins or len(self._margins) != 6:
1288 self.set_margin(refresh=False)
1289 lef, bot, rig, top, wsp, hsp = self._margins
1290 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1291 wspace=wsp,hspace=hsp)
1292
1293 tdel = max(t) - min(t)
1294 ax = PL.subplot(2,1,1)
1295 el = array(self._data.get_elevation())*180./pi
1296 PL.ylabel('El [deg.]')
1297 dstr = dates[0].strftime('%Y/%m/%d')
1298 if tdel > 1.0:
1299 dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
1300 dstr = dstr + " - " + dstr2
1301 majloc = DayLocator()
1302 minloc = HourLocator(range(0,23,12))
1303 timefmt = DateFormatter("%b%d")
1304 elif tdel > 24./60.:
1305 timefmt = DateFormatter('%H:%M')
1306 majloc = HourLocator()
1307 minloc = MinuteLocator(30)
1308 else:
1309 timefmt = DateFormatter('%H:%M')
1310 majloc = MinuteLocator(interval=5)
1311 minloc = SecondLocator(30)
1312
1313 PL.title(dstr)
1314 if tdel == 0.0:
1315 th = (t - PL.floor(t))*24.0
1316 PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
1317 else:
1318 PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
1319 #ax.grid(True)
1320 ax.xaxis.set_major_formatter(timefmt)
1321 ax.xaxis.set_major_locator(majloc)
1322 ax.xaxis.set_minor_locator(minloc)
1323 ax.yaxis.grid(True)
1324 yloc = MultipleLocator(30)
1325 ax.set_ylim(0,90)
1326 ax.yaxis.set_major_locator(yloc)
1327 if tdel > 1.0:
1328 labels = ax.get_xticklabels()
1329 # PL.setp(labels, fontsize=10, rotation=45)
1330 PL.setp(labels, fontsize=10)
1331
1332 # Az plot
1333 az = array(self._data.get_azimuth())*180./pi
1334 if min(az) < 0:
1335 for irow in range(len(az)):
1336 if az[irow] < 0: az[irow] += 360.0
1337
1338 ax2 = PL.subplot(2,1,2)
1339 #PL.xlabel('Time (UT [hour])')
1340 PL.ylabel('Az [deg.]')
1341 if tdel == 0.0:
1342 PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
1343 else:
1344 PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
1345 ax2.xaxis.set_major_formatter(timefmt)
1346 ax2.xaxis.set_major_locator(majloc)
1347 ax2.xaxis.set_minor_locator(minloc)
1348 #ax2.grid(True)
1349 ax2.set_ylim(0,360)
1350 ax2.yaxis.grid(True)
1351 #hfmt = DateFormatter('%H')
1352 #hloc = HourLocator()
1353 yloc = MultipleLocator(60)
1354 ax2.yaxis.set_major_locator(yloc)
1355 if tdel > 1.0:
1356 labels = ax2.get_xticklabels()
1357 PL.setp(labels, fontsize=10)
1358 PL.xlabel('Time (UT [day])')
1359 else:
1360 PL.xlabel('Time (UT [hour])')
1361
1362 PL.ion()
1363 PL.draw()
1364 if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
1365 if (outfile is not None):
1366 PL.savefig(outfile)
1367
1368 def plotpointing(self, scan=None, outfile=None):
1369 """
1370 plot telescope pointings
1371 """
1372 visible = rcParams['plotter.gui']
1373 from matplotlib import pylab as PL
1374 from numpy import array, pi
1375 if not visible or not self._visible:
1376 PL.ioff()
1377 from matplotlib.backends.backend_agg import FigureCanvasAgg
1378 PL.gcf().canvas.switch_backends(FigureCanvasAgg)
1379 self._data = scan
1380 dir = array(self._data.get_directionval()).transpose()
1381 ra = dir[0]*180./pi
1382 dec = dir[1]*180./pi
1383 PL.cla()
1384 #PL.ioff()
1385 PL.clf()
1386 # Adjust subplot margins
1387 if not self._margins or len(self._margins) != 6:
1388 self.set_margin(refresh=False)
1389 lef, bot, rig, top, wsp, hsp = self._margins
1390 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1391 wspace=wsp,hspace=hsp)
1392 ax = PL.gca()
1393 #ax = PL.axes([0.1,0.1,0.8,0.8])
1394 #ax = PL.axes([0.1,0.1,0.8,0.8])
1395 ax.set_aspect('equal')
1396 PL.plot(ra, dec, 'b,')
1397 PL.xlabel('RA [deg.]')
1398 PL.ylabel('Declination [deg.]')
1399 PL.title('Telescope pointings')
1400 [xmin,xmax,ymin,ymax] = PL.axis()
1401 PL.axis([xmax,xmin,ymin,ymax])
1402 PL.ion()
1403 PL.draw()
1404 if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
1405 if (outfile is not None):
1406 PL.savefig(outfile)
1407
1408 # plot total power data
1409 # plotting in time is not yet implemented..
1410 @asaplog_post_dec
1411 def plottp(self, scan=None):
1412 self._assert_plotter(action="reload")
1413 self._plotter.hold()
1414 self._plotter.clear()
1415 from asap import scantable
1416 if not self._data and not scan:
1417 msg = "Input is not a scantable"
1418 raise TypeError(msg)
1419 if isinstance(scan, scantable):
1420 if self._data is not None:
1421 if scan != self._data:
1422 self._data = scan
1423 # reset
1424 self._reset()
1425 else:
1426 self._data = scan
1427 self._reset()
1428 # ranges become invalid when abcissa changes?
1429 #if self._abcunit and self._abcunit != self._data.get_unit():
1430 # self._minmaxx = None
1431 # self._minmaxy = None
1432 # self._abcunit = self._data.get_unit()
1433 # self._datamask = None
1434
1435 # Adjust subplot margins
1436 if not self._margins or len(self._margins) !=6:
1437 self.set_margin(refresh=False)
1438 lef, bot, rig, top, wsp, hsp = self._margins
1439 self._plotter.figure.subplots_adjust(
1440 left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
1441 if self.casabar_exists(): self._plotter.figmgr.casabar.disable_button()
1442 self._plottp(self._data)
1443 if self._minmaxy is not None:
1444 self._plotter.set_limits(ylim=self._minmaxy)
1445 self._plotter.release()
1446 self._plotter.tidy()
1447 self._plotter.show(hardrefresh=False)
1448 return
1449
1450 def _plottp(self,scan):
1451 """
1452 private method for plotting total power data
1453 """
1454 from numpy import ma, array, arange, logical_not
1455 r=0
1456 nr = scan.nrow()
1457 a0,b0 = -1,-1
1458 allxlim = []
1459 allylim = []
1460 y=[]
1461 self._plotter.set_panels()
1462 self._plotter.palette(0)
1463 #title
1464 #xlab = self._abcissa and self._abcissa[panelcount] \
1465 # or scan._getabcissalabel()
1466 #ylab = self._ordinate and self._ordinate[panelcount] \
1467 # or scan._get_ordinate_label()
1468 xlab = self._abcissa or 'row number' #or Time
1469 ylab = self._ordinate or scan._get_ordinate_label()
1470 self._plotter.set_axes('xlabel',xlab)
1471 self._plotter.set_axes('ylabel',ylab)
1472 lbl = self._get_label(scan, r, 's', self._title)
1473 if isinstance(lbl, list) or isinstance(lbl, tuple):
1474 # if 0 <= panelcount < len(lbl):
1475 # lbl = lbl[panelcount]
1476 # else:
1477 # get default label
1478 lbl = self._get_label(scan, r, self._panelling, None)
1479 self._plotter.set_axes('title',lbl)
1480 y=array(scan._get_column(scan._getspectrum,-1))
1481 m = array(scan._get_column(scan._getmask,-1))
1482 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1483 x = arange(len(y))
1484 # try to handle spectral data somewhat...
1485 l,m = y.shape
1486 if m > 1:
1487 y=y.mean(axis=1)
1488 plotit = self._plotter.plot
1489 llbl = self._get_label(scan, r, self._stacking, None)
1490 self._plotter.set_line(label=llbl)
1491 if len(x) > 0:
1492 plotit(x,y)
1493
1494
1495 # forwards to matplotlib.Figure.text
1496 def figtext(self, *args, **kwargs):
1497 """
1498 Add text to figure at location x,y (relative 0-1 coords).
1499 This method forwards *args and **kwargs to a Matplotlib method,
1500 matplotlib.Figure.text.
1501 See the method help for detailed information.
1502 """
1503 self._assert_plotter(action="reload")
1504 self._plotter.text(*args, **kwargs)
1505 # end matplotlib.Figure.text forwarding function
1506
1507
1508 # printing header information
1509 @asaplog_post_dec
1510 def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
1511 """
1512 print data (scantable) header on the plot and/or logger.
1513 To plot the header on the plot, this method should be called after
1514 plotting spectra by the method, asapplotter.plot.
1515 Parameters:
1516 plot: whether or not print header info on the plot.
1517 fontsize: header font size (valid only plot=True)
1518 logger: whether or not print header info on the logger.
1519 selstr: additional selection string (not verified)
1520 extrastr: additional string to print at the beginning (not verified)
1521 """
1522 if not plot and not logger:
1523 return
1524 if not self._data:
1525 raise RuntimeError("No scantable has been set yet.")
1526 # Now header will be printed on plot and/or logger.
1527 # Get header information and format it.
1528 ssum=self._data._list_header()
1529 # Print Observation header to the upper-left corner of plot
1530 headstr=[ssum[0:ssum.find('Obs. Type:')]]
1531 headstr.append(ssum[ssum.find('Obs. Type:'):ssum.find('Flux Unit:')])
1532 if extrastr != '':
1533 headstr[0]=extrastr+'\n'+headstr[0]
1534 self._headtext['extrastr'] = extrastr
1535 if selstr != '':
1536 selstr += '\n'
1537 self._headtext['selstr'] = selstr
1538 ssel=(selstr+self._data.get_selection().__str__()+self._selection.__str__() or 'none')
1539 headstr.append('***Selections***\n'+ssel)
1540
1541 if plot:
1542 errmsg = "Can plot header only after the first call to plot()."
1543 self._assert_plotter(action="halt",errmsg=errmsg)
1544 self._plotter.hold()
1545 self._header_plot(headstr,fontsize=fontsize)
1546 import time
1547 self._plotter.figure.text(0.99,0.01,
1548 time.strftime("%a %d %b %Y %H:%M:%S %Z"),
1549 horizontalalignment='right',
1550 verticalalignment='bottom',fontsize=8)
1551 self._plotter.release()
1552 if logger:
1553 selstr = "Selections: "+ssel
1554 asaplog.push("----------------\n Plot Summary\n----------------")
1555 asaplog.push(extrastr)
1556 asaplog.push(ssum[0:ssum.find('Selection:')]\
1557 + selstr)
1558 self._headtext['string'] = headstr
1559 del ssel, ssum, headstr
1560
1561 def _header_plot(self, texts, fontsize=9):
1562 self._headtext['textobj']=[]
1563 nstcol=len(texts)
1564 for i in range(nstcol):
1565 self._headtext['textobj'].append(
1566 self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1567 texts[i],
1568 horizontalalignment='left',
1569 verticalalignment='top',
1570 fontsize=fontsize))
1571
1572 def clear_header(self):
1573 if not self._headtext['textobj']:
1574 asaplog.push("No header has been plotted. Exit without any operation")
1575 asaplog.post("WARN")
1576 elif self._assert_plotter(action="status"):
1577 self._plotter.hold()
1578 for textobj in self._headtext['textobj']:
1579 #if textobj.get_text() in self._headstring:
1580 try:
1581 textobj.remove()
1582 except NotImplementedError:
1583 self._plotter.figure.texts.pop(self._plotter.figure.texts.index(textobj))
1584 self._plotter.release()
1585 self._reset_header()
1586
1587 # plot spectra by pointing
1588 @asaplog_post_dec
1589 def plotgrid(self, scan=None,center=None,spacing=None,rows=None,cols=None):
1590 """
1591 Plot spectra based on direction.
1592
1593 Parameters:
1594 scan: a scantable to plot
1595 center: the grid center direction (a list) of plots in the
1596 unit of DIRECTION column.
1597 (default) the center of map region
1598 spacing: a list of horizontal (R.A.) and vertical (Dec.)
1599 spacing in the unit of DIRECTION column.
1600 (default) Calculated by the extent of map region and
1601 the number of rows and cols to cover
1602 rows: number of panels (grid points) in horizontal direction
1603 cols: number of panels (grid points) in vertical direction
1604
1605 Note:
1606 - Only the first IFNO, POLNO, and BEAM in the scantable will be
1607 plotted.
1608 - This method doesn't re-grid and average spectra in scantable. Use
1609 asapgrid module to re-grid spectra before plotting with this method.
1610 Only the first spectrum is plotted in case there are multiple
1611 spectra which belong to a grid.
1612 """
1613 from asap import scantable
1614 from numpy import array, ma
1615 if not self._data and not scan:
1616 msg = "No scantable is specified to plot"
1617 raise TypeError(msg)
1618 if scan:
1619 self.set_data(scan, refresh=False)
1620 del scan
1621
1622 # Rows and cols
1623 if rows:
1624 self._rows = int(rows)
1625 else:
1626 msg = "Number of rows to plot are not specified. "
1627 if self._rows:
1628 msg += "Using previous value = %d" % (self._rows)
1629 asaplog.push(msg)
1630 else:
1631 self._rows = 1
1632 msg += "Setting rows = %d" % (self._rows)
1633 asaplog.post()
1634 asaplog.push(msg)
1635 asaplog.post("WARN")
1636 if cols:
1637 self._cols = int(cols)
1638 else:
1639 msg = "Number of cols to plot are not specified. "
1640 if self._cols:
1641 msg += "Using previous value = %d" % (self._cols)
1642 asaplog.push(msg)
1643 else:
1644 self._cols = 1
1645 msg += "Setting cols = %d" % (self._cols)
1646 asaplog.post()
1647 asaplog.push(msg)
1648 asaplog.post("WARN")
1649
1650 # Center and spacing
1651 if type(center) == list and len(center) > 1:
1652 center = center[0:2]
1653 else:
1654 asaplog.post()
1655 asaplog.push("Grid center is not specified. Automatically calculated from pointing center.")
1656 asaplog.post("WARN")
1657 dirarr = array(self._data.get_directionval()).transpose()
1658 #center = [dirarr[0].mean(), dirarr[1].mean()]
1659 center = [0.5*(dirarr[0].max() + dirarr[0].min()),
1660 0.5*(dirarr[1].max() + dirarr[1].min())]
1661 del dirarr
1662 asaplog.push("Grid center: (%f, %f) " % (center[0],center[1]))
1663
1664 if spacing is None:
1665 asaplog.post()
1666 asaplog.push("Grid spacing not specified. Automatically calculated from map coverage")
1667 asaplog.post("WARN")
1668 # automatically get spacing
1669 dirarr = array(self._data.get_directionval()).transpose()
1670 wx = 2. * max(abs(dirarr[0].max()-center[0]),
1671 abs(dirarr[0].min()-center[0]))
1672 wy = 2. * max(abs(dirarr[1].max()-center[1]),
1673 abs(dirarr[1].min()-center[1]))
1674 # slightly expand area to plot the edges
1675 wx *= 1.01
1676 wy *= 1.01
1677 xgrid = wx/self._cols
1678 ygrid = wy/self._rows
1679 #print "Pointing range: (x, y) = (%f - %f, %f - %f)" %\
1680 # (dirarr[0].min(),dirarr[0].max(),dirarr[1].min(),dirarr[1].max())
1681 # identical R.A. and/or Dec. for all spectra.
1682 if xgrid == 0:
1683 xgrid = 1.
1684 if ygrid == 0:
1685 ygrid = 1.
1686 # spacing should be negative to transpose plot
1687 spacing = [- xgrid, - ygrid]
1688 del dirarr, xgrid, ygrid
1689 #elif isinstance(spacing, str):
1690 # # spacing is a quantity
1691 elif isinstance(spacing,list) and len(spacing) > 1:
1692 for i in xrange(2):
1693 val = spacing[i]
1694 if not isinstance(val, float):
1695 raise TypeError("spacing should be a list of float")
1696 if val > 0.:
1697 spacing[i] = -val
1698 spacing = spacing[0:2]
1699 else:
1700 msg = "Invalid spacing."
1701 raise TypeError(msg)
1702 asaplog.push("Spacing: (%f, %f) " % (spacing[0],spacing[1]))
1703
1704 ntotpl = self._rows * self._cols
1705 minpos = [center[0]-spacing[0]*self._cols/2.,
1706 center[1]-spacing[1]*self._rows/2.]
1707 #xbound = [center[0]-spacing[0]*self._cols/2.,
1708 # center[0]+spacing[0]*self._cols/2.]
1709 #ybound = [center[1]-spacing[1]*self._rows/2.,
1710 # center[1]+spacing[1]*self._rows/2.]
1711 #print "Plot range: (x, y) = (%f - %f, %f - %f)" %\
1712 # (minpos[0],minpos[0]+spacing[0]*self._cols,
1713 # minpos[1],minpos[1]+spacing[1]*self._rows)
1714 ## (xbound[0],xbound[1],ybound[0],ybound[1])
1715 ifs = self._data.getifnos()
1716 if len(ifs) > 1:
1717 msg = "Found multiple IFs in scantable. Only the first IF (IFNO=%d) will be plotted." % ifs[0]
1718 asaplog.post()
1719 asaplog.push(msg)
1720 asaplog.post("WARN")
1721 pols = self._data.getpolnos()
1722 if len(pols) > 1:
1723 msg = "Found multiple POLs in scantable. Only the first POL (POLNO=%d) will be plotted." % pols[0]
1724 asaplog.post()
1725 asaplog.push(msg)
1726 asaplog.post("WARN")
1727 beams = self._data.getbeamnos()
1728 if len(beams) > 1:
1729 msg = "Found multiple BEAMs in scantable. Only the first BEAM (BEAMNO=%d) will be plotted." % beams[0]
1730 asaplog.post()
1731 asaplog.push(msg)
1732 asaplog.post("WARN")
1733 self._data.set_selection(ifs=[ifs[0]],pols=[pols[0]],beams=[beams[0]])
1734 if self._data.nrow() > ntotpl:
1735 msg = "Scantable is finely sampled than plotting grids. "\
1736 + "Only the first spectrum is plotted in each grid."
1737 asaplog.post()
1738 asaplog.push(msg)
1739 asaplog.post("WARN")
1740
1741 self._assert_plotter(action="reload")
1742 self._plotter.hold()
1743 self._plotter.clear()
1744 self._plotter.legend()
1745
1746 # Adjust subplot margins
1747 if not self._margins or len(self._margins) !=6:
1748 self.set_margin(refresh=False)
1749 self._plotter.set_panels(rows=self._rows,cols=self._cols,
1750 nplots=ntotpl,margin=self._margins,ganged=True)
1751 if self.casabar_exists():
1752 #self._plotter.figmgr.casabar.disable_button()
1753 self._plotter.figmgr.casabar.set_pagecounter(1)
1754 self._plotter.figmgr.casabar.enable_button()
1755 # Actual plot
1756 npl = 0
1757 for irow in range(self._data.nrow()):
1758 pos = self._data.get_directionval(irow)
1759 ix = int((pos[0] - minpos[0])/spacing[0])
1760 #if pos[0] < xbound[0] or pos[0] > xbound[1]:
1761 if ix < 0 or ix >= self._cols:
1762 #print "Row %d : Out of X-range (x = %f) ... skipped" % (irow, pos[0])
1763 continue
1764 #ix = min(int((pos[0] - xbound[0])/spacing[0]),self._cols)
1765 iy = int((pos[1]- minpos[1])/spacing[1])
1766 #if pos[1] < ybound[0] or pos[1] > ybound[1]:
1767 if iy < 0 or iy >= self._cols:
1768 #print "Row %d : Out of Y-range (y = %f) ... skipped" % (irow,pos[1])
1769 continue
1770 #iy = min(int((pos[1]- ybound[0])/spacing[1]),self._rows)
1771 ipanel = ix + iy*self._cols
1772 if len(self._plotter.subplots[ipanel]['lines']) > 0:
1773 #print "Row %d : panel %d lready plotted ... skipped" % (irow,ipanel)
1774 # a spectrum already plotted in the panel
1775 continue
1776 # Plotting this row
1777 #print "PLOTTING row %d (panel=%d)" % (irow, ipanel)
1778 npl += 1
1779 self._plotter.subplot(ipanel)
1780 self._plotter.palette(0,colormap=self._colormap, \
1781 linestyle=0,linestyles=self._linestyles)
1782 xlab = self._abcissa and self._abcissa[ipanel] \
1783 or self._data._getabcissalabel(irow)
1784 if self._offset and not self._abcissa:
1785 xlab += " (relative)"
1786 ylab = self._ordinate and self._ordinate[ipanel] \
1787 or self._data._get_ordinate_label()
1788 self._plotter.set_axes('xlabel', xlab)
1789 self._plotter.set_axes('ylabel', ylab)
1790 #from numpy import pi
1791 #lbl = "(%f, %f)" % (self._data.get_directionval(irow)[0]*180/pi,self._data.get_directionval(irow)[1]*180./pi)
1792 lbl = self._data.get_direction(irow)
1793 self._plotter.set_axes('title',lbl)
1794
1795 y = self._data._getspectrum(irow)
1796 # flag application
1797 mr = self._data._getflagrow(irow)
1798 if mr: # FLAGROW=True
1799 y = ma.masked_array(y,mask=mr)
1800 else:
1801 m = self._data._getmask(irow)
1802 from numpy import logical_not, logical_and
1803 ### user mask is not available so far
1804 #if self._maskselection and len(self._usermask) == len(m):
1805 # if d[self._stacking](irow) in self._maskselection[self._stacking]:
1806 # m = logical_and(m, self._usermask)
1807 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1808
1809 x = array(self._data._getabcissa(irow))
1810 if self._offset:
1811 x += self._offset
1812 if self._minmaxx is not None:
1813 s,e = self._slice_indeces(x)
1814 x = x[s:e]
1815 y = y[s:e]
1816 if len(x) > 1024 and rcParams['plotter.decimate']:
1817 fac = len(x)/1024
1818 x = x[::fac]
1819 y = y[::fac]
1820 self._plotter.set_line(label=lbl)
1821 plotit = self._plotter.plot
1822 if self._hist: plotit = self._plotter.hist
1823 if len(x) > 0 and not mr:
1824 plotit(x,y)
1825# xlim= self._minmaxx or [min(x),max(x)]
1826# allxlim += xlim
1827# ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
1828# allylim += ylim
1829# else:
1830# xlim = self._minmaxx or []
1831# allxlim += xlim
1832# ylim= self._minmaxy or []
1833# allylim += ylim
1834
1835 if npl >= ntotpl:
1836 break
1837
1838 if self._minmaxy is not None:
1839 self._plotter.set_limits(ylim=self._minmaxy)
1840 self._plotter.release()
1841 self._plotter.tidy()
1842 self._plotter.show(hardrefresh=False)
1843 return
Note: See TracBrowser for help on using the repository browser.