source: trunk/python/asapplotter.py@ 2640

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

New Development: No

JIRA Issue: No (removal of debug comments)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): asapplotter, flagplotter, sdplot, and sdflag

Description:

Removed debug comments and destructor of asapplotter.


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