source: trunk/python/asapplotter.py@ 2675

Last change on this file since 2675 was 2650, checked in by Malte Marquarding, 12 years ago

Issue #278: don't reset plotter on set_selection; use combined data and plotter selection to plot.

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