source: trunk/python/asapplotter.py@ 2592

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

New Development: No

JIRA Issue: No (for a new darwin11 @ CASA for OSX)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: test_sdplot[testplot01], or run asap.asapplotter.plot_azel with matplotlib1.1

Put in Release Notes: No

Module(s): sdplot (CASA) and asapplotter

Description:

Handled update of matplotlib version in a new darwin11 package (distributed
in Jul., 2012).
A method, timezone, doesn't exists in matplotlib.dates, and
pytz.timezone should be called instead.


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