source: trunk/python/asapplotter.py@ 2578

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

New Development: Yes

JIRA Issue: Yes (CAS-1814/ATNF Trac #271)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: added a new method, asapplotter.plotgrid

Test Programs:

Put in Release Notes: No

Module(s): asapplotter, sdplot

Description:

Added a new method, plotgrid, in the asapplotter class.
The methods samples spectra in a scantable by sky position recorded in
the DIRECTION column and plot them. User can specify center and spacing in
sky position to sample spectra. The number of spectra to be sampled in
horizontal (R.A.) and vertical (Dec.) direction should be specified as well.
An example of usage is,

import asap as sd
scan = sd.scantable("SOME_NAME",average=False)
sd.asapplotter.plotgrid(scan,center=(-3.133363,-0.329469),spacing=(-0.000272,-0.000257),rows=8,cols=8)

This example will sample 8x8 spectra and plot them by sky position.
So far, center and spacing should be defined in the unit of DIRECTION column.

  • 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, timezone
1270 from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
1271 from matplotlib.ticker import MultipleLocator
1272 from numpy import array, pi
1273 if not visible or not self._visible:
1274 PL.ioff()
1275 from matplotlib.backends.backend_agg import FigureCanvasAgg
1276 PL.gcf().canvas.switch_backends(FigureCanvasAgg)
1277 self._data = scan
1278 dates = self._data.get_time(asdatetime=True)
1279 t = PL.date2num(dates)
1280 tz = timezone('UTC')
1281 PL.cla()
1282 PL.ioff()
1283 PL.clf()
1284 # Adjust subplot margins
1285 if not self._margins or len(self._margins) != 6:
1286 self.set_margin(refresh=False)
1287 lef, bot, rig, top, wsp, hsp = self._margins
1288 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1289 wspace=wsp,hspace=hsp)
1290
1291 tdel = max(t) - min(t)
1292 ax = PL.subplot(2,1,1)
1293 el = array(self._data.get_elevation())*180./pi
1294 PL.ylabel('El [deg.]')
1295 dstr = dates[0].strftime('%Y/%m/%d')
1296 if tdel > 1.0:
1297 dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
1298 dstr = dstr + " - " + dstr2
1299 majloc = DayLocator()
1300 minloc = HourLocator(range(0,23,12))
1301 timefmt = DateFormatter("%b%d")
1302 elif tdel > 24./60.:
1303 timefmt = DateFormatter('%H:%M')
1304 majloc = HourLocator()
1305 minloc = MinuteLocator(30)
1306 else:
1307 timefmt = DateFormatter('%H:%M')
1308 majloc = MinuteLocator(interval=5)
1309 minloc = SecondLocator(30)
1310
1311 PL.title(dstr)
1312 if tdel == 0.0:
1313 th = (t - PL.floor(t))*24.0
1314 PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
1315 else:
1316 PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
1317 #ax.grid(True)
1318 ax.xaxis.set_major_formatter(timefmt)
1319 ax.xaxis.set_major_locator(majloc)
1320 ax.xaxis.set_minor_locator(minloc)
1321 ax.yaxis.grid(True)
1322 yloc = MultipleLocator(30)
1323 ax.set_ylim(0,90)
1324 ax.yaxis.set_major_locator(yloc)
1325 if tdel > 1.0:
1326 labels = ax.get_xticklabels()
1327 # PL.setp(labels, fontsize=10, rotation=45)
1328 PL.setp(labels, fontsize=10)
1329
1330 # Az plot
1331 az = array(self._data.get_azimuth())*180./pi
1332 if min(az) < 0:
1333 for irow in range(len(az)):
1334 if az[irow] < 0: az[irow] += 360.0
1335
1336 ax2 = PL.subplot(2,1,2)
1337 #PL.xlabel('Time (UT [hour])')
1338 PL.ylabel('Az [deg.]')
1339 if tdel == 0.0:
1340 PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
1341 else:
1342 PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
1343 ax2.xaxis.set_major_formatter(timefmt)
1344 ax2.xaxis.set_major_locator(majloc)
1345 ax2.xaxis.set_minor_locator(minloc)
1346 #ax2.grid(True)
1347 ax2.set_ylim(0,360)
1348 ax2.yaxis.grid(True)
1349 #hfmt = DateFormatter('%H')
1350 #hloc = HourLocator()
1351 yloc = MultipleLocator(60)
1352 ax2.yaxis.set_major_locator(yloc)
1353 if tdel > 1.0:
1354 labels = ax2.get_xticklabels()
1355 PL.setp(labels, fontsize=10)
1356 PL.xlabel('Time (UT [day])')
1357 else:
1358 PL.xlabel('Time (UT [hour])')
1359
1360 PL.ion()
1361 PL.draw()
1362 if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
1363 if (outfile is not None):
1364 PL.savefig(outfile)
1365
1366 def plotpointing(self, scan=None, outfile=None):
1367 """
1368 plot telescope pointings
1369 """
1370 visible = rcParams['plotter.gui']
1371 from matplotlib import pylab as PL
1372 from numpy import array, pi
1373 if not visible or not self._visible:
1374 PL.ioff()
1375 from matplotlib.backends.backend_agg import FigureCanvasAgg
1376 PL.gcf().canvas.switch_backends(FigureCanvasAgg)
1377 self._data = scan
1378 dir = array(self._data.get_directionval()).transpose()
1379 ra = dir[0]*180./pi
1380 dec = dir[1]*180./pi
1381 PL.cla()
1382 #PL.ioff()
1383 PL.clf()
1384 # Adjust subplot margins
1385 if not self._margins or len(self._margins) != 6:
1386 self.set_margin(refresh=False)
1387 lef, bot, rig, top, wsp, hsp = self._margins
1388 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1389 wspace=wsp,hspace=hsp)
1390 ax = PL.gca()
1391 #ax = PL.axes([0.1,0.1,0.8,0.8])
1392 #ax = PL.axes([0.1,0.1,0.8,0.8])
1393 ax.set_aspect('equal')
1394 PL.plot(ra, dec, 'b,')
1395 PL.xlabel('RA [deg.]')
1396 PL.ylabel('Declination [deg.]')
1397 PL.title('Telescope pointings')
1398 [xmin,xmax,ymin,ymax] = PL.axis()
1399 PL.axis([xmax,xmin,ymin,ymax])
1400 PL.ion()
1401 PL.draw()
1402 if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
1403 if (outfile is not None):
1404 PL.savefig(outfile)
1405
1406 # plot total power data
1407 # plotting in time is not yet implemented..
1408 @asaplog_post_dec
1409 def plottp(self, scan=None):
1410 self._assert_plotter(action="reload")
1411 self._plotter.hold()
1412 self._plotter.clear()
1413 from asap import scantable
1414 if not self._data and not scan:
1415 msg = "Input is not a scantable"
1416 raise TypeError(msg)
1417 if isinstance(scan, scantable):
1418 if self._data is not None:
1419 if scan != self._data:
1420 self._data = scan
1421 # reset
1422 self._reset()
1423 else:
1424 self._data = scan
1425 self._reset()
1426 # ranges become invalid when abcissa changes?
1427 #if self._abcunit and self._abcunit != self._data.get_unit():
1428 # self._minmaxx = None
1429 # self._minmaxy = None
1430 # self._abcunit = self._data.get_unit()
1431 # self._datamask = None
1432
1433 # Adjust subplot margins
1434 if not self._margins or len(self._margins) !=6:
1435 self.set_margin(refresh=False)
1436 lef, bot, rig, top, wsp, hsp = self._margins
1437 self._plotter.figure.subplots_adjust(
1438 left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
1439 if self.casabar_exists(): self._plotter.figmgr.casabar.disable_button()
1440 self._plottp(self._data)
1441 if self._minmaxy is not None:
1442 self._plotter.set_limits(ylim=self._minmaxy)
1443 self._plotter.release()
1444 self._plotter.tidy()
1445 self._plotter.show(hardrefresh=False)
1446 return
1447
1448 def _plottp(self,scan):
1449 """
1450 private method for plotting total power data
1451 """
1452 from numpy import ma, array, arange, logical_not
1453 r=0
1454 nr = scan.nrow()
1455 a0,b0 = -1,-1
1456 allxlim = []
1457 allylim = []
1458 y=[]
1459 self._plotter.set_panels()
1460 self._plotter.palette(0)
1461 #title
1462 #xlab = self._abcissa and self._abcissa[panelcount] \
1463 # or scan._getabcissalabel()
1464 #ylab = self._ordinate and self._ordinate[panelcount] \
1465 # or scan._get_ordinate_label()
1466 xlab = self._abcissa or 'row number' #or Time
1467 ylab = self._ordinate or scan._get_ordinate_label()
1468 self._plotter.set_axes('xlabel',xlab)
1469 self._plotter.set_axes('ylabel',ylab)
1470 lbl = self._get_label(scan, r, 's', self._title)
1471 if isinstance(lbl, list) or isinstance(lbl, tuple):
1472 # if 0 <= panelcount < len(lbl):
1473 # lbl = lbl[panelcount]
1474 # else:
1475 # get default label
1476 lbl = self._get_label(scan, r, self._panelling, None)
1477 self._plotter.set_axes('title',lbl)
1478 y=array(scan._get_column(scan._getspectrum,-1))
1479 m = array(scan._get_column(scan._getmask,-1))
1480 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1481 x = arange(len(y))
1482 # try to handle spectral data somewhat...
1483 l,m = y.shape
1484 if m > 1:
1485 y=y.mean(axis=1)
1486 plotit = self._plotter.plot
1487 llbl = self._get_label(scan, r, self._stacking, None)
1488 self._plotter.set_line(label=llbl)
1489 if len(x) > 0:
1490 plotit(x,y)
1491
1492
1493 # forwards to matplotlib.Figure.text
1494 def figtext(self, *args, **kwargs):
1495 """
1496 Add text to figure at location x,y (relative 0-1 coords).
1497 This method forwards *args and **kwargs to a Matplotlib method,
1498 matplotlib.Figure.text.
1499 See the method help for detailed information.
1500 """
1501 self._assert_plotter(action="reload")
1502 self._plotter.text(*args, **kwargs)
1503 # end matplotlib.Figure.text forwarding function
1504
1505
1506 # printing header information
1507 @asaplog_post_dec
1508 def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
1509 """
1510 print data (scantable) header on the plot and/or logger.
1511 To plot the header on the plot, this method should be called after
1512 plotting spectra by the method, asapplotter.plot.
1513 Parameters:
1514 plot: whether or not print header info on the plot.
1515 fontsize: header font size (valid only plot=True)
1516 logger: whether or not print header info on the logger.
1517 selstr: additional selection string (not verified)
1518 extrastr: additional string to print at the beginning (not verified)
1519 """
1520 if not plot and not logger:
1521 return
1522 if not self._data:
1523 raise RuntimeError("No scantable has been set yet.")
1524 # Now header will be printed on plot and/or logger.
1525 # Get header information and format it.
1526 ssum=self._data._list_header()
1527 # Print Observation header to the upper-left corner of plot
1528 headstr=[ssum[0:ssum.find('Obs. Type:')]]
1529 headstr.append(ssum[ssum.find('Obs. Type:'):ssum.find('Flux Unit:')])
1530 if extrastr != '':
1531 headstr[0]=extrastr+'\n'+headstr[0]
1532 self._headtext['extrastr'] = extrastr
1533 if selstr != '':
1534 selstr += '\n'
1535 self._headtext['selstr'] = selstr
1536 ssel=(selstr+self._data.get_selection().__str__()+self._selection.__str__() or 'none')
1537 headstr.append('***Selections***\n'+ssel)
1538
1539 if plot:
1540 errmsg = "Can plot header only after the first call to plot()."
1541 self._assert_plotter(action="halt",errmsg=errmsg)
1542 self._plotter.hold()
1543 self._header_plot(headstr,fontsize=fontsize)
1544 import time
1545 self._plotter.figure.text(0.99,0.01,
1546 time.strftime("%a %d %b %Y %H:%M:%S %Z"),
1547 horizontalalignment='right',
1548 verticalalignment='bottom',fontsize=8)
1549 self._plotter.release()
1550 if logger:
1551 selstr = "Selections: "+ssel
1552 asaplog.push("----------------\n Plot Summary\n----------------")
1553 asaplog.push(extrastr)
1554 asaplog.push(ssum[0:ssum.find('Selection:')]\
1555 + selstr)
1556 self._headtext['string'] = headstr
1557 del ssel, ssum, headstr
1558
1559 def _header_plot(self, texts, fontsize=9):
1560 self._headtext['textobj']=[]
1561 nstcol=len(texts)
1562 for i in range(nstcol):
1563 self._headtext['textobj'].append(
1564 self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1565 texts[i],
1566 horizontalalignment='left',
1567 verticalalignment='top',
1568 fontsize=fontsize))
1569
1570 def clear_header(self):
1571 if not self._headtext['textobj']:
1572 asaplog.push("No header has been plotted. Exit without any operation")
1573 asaplog.post("WARN")
1574 elif self._assert_plotter(action="status"):
1575 self._plotter.hold()
1576 for textobj in self._headtext['textobj']:
1577 #if textobj.get_text() in self._headstring:
1578 try:
1579 textobj.remove()
1580 except NotImplementedError:
1581 self._plotter.figure.texts.pop(self._plotter.figure.texts.index(textobj))
1582 self._plotter.release()
1583 self._reset_header()
1584
1585 # plot spectra by pointing
1586 @asaplog_post_dec
1587 def plotgrid(self, scan=None,center=None,spacing=None,rows=None,cols=None):
1588 """
1589 Plot spectra based on direction.
1590
1591 Parameters:
1592 scan: a scantable to plot
1593 center: the grid center direction (a list) of plots in the
1594 unit of DIRECTION column.
1595 (default) the center of map region
1596 spacing: a list of horizontal (R.A.) and vertical (Dec.)
1597 spacing in the unit of DIRECTION column.
1598 (default) Calculated by the extent of map region and
1599 the number of rows and cols to cover
1600 rows: number of panels (grid points) in horizontal direction
1601 cols: number of panels (grid points) in vertical direction
1602
1603 Note:
1604 - Only the first IFNO, POLNO, and BEAM in the scantable will be
1605 plotted.
1606 - This method doesn't re-grid and average spectra in scantable. Use
1607 asapgrid module to re-grid spectra before plotting with this method.
1608 Only the first spectrum is plotted in case there are multiple
1609 spectra which belong to a grid.
1610 """
1611 from asap import scantable
1612 from numpy import array, ma
1613 if not self._data and not scan:
1614 msg = "No scantable is specified to plot"
1615 raise TypeError(msg)
1616 if isinstance(scan, scantable):
1617 if self._data is not None:
1618 if scan != self._data:
1619 self._data = scan
1620 # reset
1621 self._reset()
1622 else:
1623 self._data = scan
1624 self._reset()
1625 elif not self._data:
1626 msg = "Input is not a scantable"
1627 raise TypeError(msg)
1628
1629 # Rows and cols
1630 if rows:
1631 self._rows = int(rows)
1632 else:
1633 msg = "Number of rows to plot are not specified. "
1634 if self._rows:
1635 msg += "Using previous value = %d" % (self._rows)
1636 asaplog.push(msg)
1637 else:
1638 self._rows = 1
1639 msg += "Setting rows = %d" % (self._rows)
1640 asaplog.post()
1641 asaplog.push(msg)
1642 asaplog.post("WARN")
1643 if cols:
1644 self._cols = int(cols)
1645 else:
1646 msg = "Number of cols to plot are not specified. "
1647 if self._cols:
1648 msg += "Using previous value = %d" % (self._cols)
1649 asaplog.push(msg)
1650 else:
1651 self._cols = 1
1652 msg += "Setting cols = %d" % (self._cols)
1653 asaplog.post()
1654 asaplog.push(msg)
1655 asaplog.post("WARN")
1656
1657 # Center and spacing
1658 if type(center) == list and len(center) > 1:
1659 center = center[0:2]
1660 else:
1661 asaplog.post()
1662 asaplog.push("Grid center is not specified. Automatically calculated from pointing center.")
1663 asaplog.post("WARN")
1664 dirarr = array(self._data.get_directionval()).transpose()
1665 #center = [dirarr[0].mean(), dirarr[1].mean()]
1666 center = [0.5*(dirarr[0].max() + dirarr[0].min()),
1667 0.5*(dirarr[1].max() + dirarr[1].min())]
1668 del dirarr
1669 asaplog.push("Grid center: (%f, %f) " % (center[0],center[1]))
1670
1671 if spacing is None:
1672 asaplog.post()
1673 asaplog.push("Grid spacing not specified. Automatically calculated from map coverage")
1674 asaplog.post("WARN")
1675 # automatically get spacing
1676 dirarr = array(self._data.get_directionval()).transpose()
1677 wx = 2. * max(abs(dirarr[0].max()-center[0]),
1678 abs(dirarr[0].min()-center[0]))
1679 wy = 2. * max(abs(dirarr[1].max()-center[1]),
1680 abs(dirarr[1].min()-center[1]))
1681 # slightly expand area to plot the edges
1682 wx *= 1.01
1683 wy *= 1.01
1684 xgrid = wx/self._cols
1685 ygrid = wy/self._rows
1686 print "Pointing range: (x, y) = (%f - %f, %f - %f)" %\
1687 (dirarr[0].min(),dirarr[0].max(),dirarr[1].min(),dirarr[1].max())
1688 # identical R.A. and/or Dec. for all spectra.
1689 if xgrid == 0:
1690 xgrid = 1.
1691 if ygrid == 0:
1692 ygrid = 1.
1693 # spacing should be negative to transpose plot
1694 spacing = [- xgrid, - ygrid]
1695 del dirarr, xgrid, ygrid
1696 #elif isinstance(spacing, str):
1697 # # spacing is a quantity
1698 elif isinstance(spacing,list) and len(spacing) > 1:
1699 for val in spacing[0:2]:
1700 if not isinstance(val, str):
1701 raise TypeError("spacing should be a list of float")
1702 spacing = spacing[0:2]
1703 else:
1704 msg = "Invalid spacing."
1705 raise TypeError(msg)
1706 asaplog.push("Spacing: (%f, %f) " % (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 #xbound = [center[0]-spacing[0]*self._cols/2.,
1712 # center[0]+spacing[0]*self._cols/2.]
1713 #ybound = [center[1]-spacing[1]*self._rows/2.,
1714 # center[1]+spacing[1]*self._rows/2.]
1715 print "Plot range: (x, y) = (%f - %f, %f - %f)" %\
1716 (minpos[0],minpos[0]+spacing[0]*self._cols,
1717 minpos[1],minpos[1]+spacing[1]*self._rows)
1718 # (xbound[0],xbound[1],ybound[0],ybound[1])
1719 ifs = self._data.getifnos()
1720 if len(ifs) > 1:
1721 msg = "Found multiple IFs in scantable. Only the first IF (IFNO=%d) will be plotted." % ifs[0]
1722 asaplog.post()
1723 asaplog.push(msg)
1724 asaplog.post("WARN")
1725 pols = self._data.getpolnos()
1726 if len(pols) > 1:
1727 msg = "Found multiple POLs in scantable. Only the first POL (POLNO=%d) will be plotted." % pols[0]
1728 asaplog.post()
1729 asaplog.push(msg)
1730 asaplog.post("WARN")
1731 beams = self._data.getbeamnos()
1732 if len(beams) > 1:
1733 msg = "Found multiple BEAMs in scantable. Only the first BEAM (BEAMNO=%d) will be plotted." % beams[0]
1734 asaplog.post()
1735 asaplog.push(msg)
1736 asaplog.post("WARN")
1737 self._data.set_selection(ifs=[ifs[0]],pols=[pols[0]],beams=[beams[0]])
1738 if self._data.nrow() > ntotpl:
1739 msg = "Scantable is finely sampled than plotting grids. "\
1740 + "Only the first spectrum is plotted in each grid."
1741 asaplog.post()
1742 asaplog.push(msg)
1743 asaplog.post("WARN")
1744
1745 self._assert_plotter(action="reload")
1746 self._plotter.hold()
1747 self._plotter.clear()
1748
1749 # Adjust subplot margins
1750 if not self._margins or len(self._margins) !=6:
1751 self.set_margin(refresh=False)
1752 self._plotter.set_panels(rows=self._rows,cols=self._cols,
1753 nplots=ntotpl,margin=self._margins,ganged=True)
1754 if self.casabar_exists(): self._plotter.figmgr.casabar.disable_button()
1755 # Actual plot
1756 npl = 0
1757 for irow in range(self._data.nrow()):
1758 pos = self._data.get_directionval(irow)
1759 ix = int((pos[0] - minpos[0])/spacing[0])
1760 #if pos[0] < xbound[0] or pos[0] > xbound[1]:
1761 if ix < 0 or ix >= self._cols:
1762 print "Row %d : Out of X-range (x = %f) ... skipped" % (irow, pos[0])
1763 continue
1764 #ix = min(int((pos[0] - xbound[0])/spacing[0]),self._cols)
1765 iy = int((pos[1]- minpos[1])/spacing[1])
1766 #if pos[1] < ybound[0] or pos[1] > ybound[1]:
1767 if iy < 0 or iy >= self._cols:
1768 print "Row %d : Out of Y-range (y = %f) ... skipped" % (irow,pos[1])
1769 continue
1770 #iy = min(int((pos[1]- ybound[0])/spacing[1]),self._rows)
1771 ipanel = ix + iy*self._cols
1772 if len(self._plotter.subplots[ipanel]['lines']) > 0:
1773 print "Row %d : panel %d lready plotted ... skipped" % (irow,ipanel)
1774 # a spectrum already plotted in the panel
1775 continue
1776 # Plotting this row
1777 print "PLOTTING row %d (panel=%d)" % (irow, ipanel)
1778 npl += 1
1779 self._plotter.subplot(ipanel)
1780 self._plotter.palette(0)
1781 xlab = self._abcissa and self._abcissa[ipanel] \
1782 or scan._getabcissalabel(irow)
1783 if self._offset and not self._abcissa:
1784 xlab += " (relative)"
1785 ylab = self._ordinate and self._ordinate[ipanel] \
1786 or scan._get_ordinate_label()
1787 self._plotter.set_axes('xlabel', xlab)
1788 self._plotter.set_axes('ylabel', ylab)
1789 #from numpy import pi
1790 #lbl = "(%f, %f)" % (self._data.get_directionval(irow)[0]*180/pi,self._data.get_directionval(irow)[1]*180./pi)
1791 lbl = self._data.get_direction(irow)
1792 self._plotter.set_axes('title',lbl)
1793
1794 y = scan._getspectrum(irow)
1795 # flag application
1796 mr = scan._getflagrow(irow)
1797 if mr: # FLAGROW=True
1798 y = ma.masked_array(y,mask=mr)
1799 else:
1800 m = scan._getmask(irow)
1801 from numpy import logical_not, logical_and
1802 ### user mask is not available so far
1803 #if self._maskselection and len(self._usermask) == len(m):
1804 # if d[self._stacking](irow) in self._maskselection[self._stacking]:
1805 # m = logical_and(m, self._usermask)
1806 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1807
1808 x = array(scan._getabcissa(irow))
1809 if self._offset:
1810 x += self._offset
1811 if self._minmaxx is not None:
1812 s,e = self._slice_indeces(x)
1813 x = x[s:e]
1814 y = y[s:e]
1815 if len(x) > 1024 and rcParams['plotter.decimate']:
1816 fac = len(x)/1024
1817 x = x[::fac]
1818 y = y[::fac]
1819 self._plotter.set_line(label=lbl)
1820 plotit = self._plotter.plot
1821 if self._hist: plotit = self._plotter.hist
1822 if len(x) > 0 and not mr:
1823 plotit(x,y)
1824# xlim= self._minmaxx or [min(x),max(x)]
1825# allxlim += xlim
1826# ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
1827# allylim += ylim
1828# else:
1829# xlim = self._minmaxx or []
1830# allxlim += xlim
1831# ylim= self._minmaxy or []
1832# allylim += ylim
1833
1834 if npl >= ntotpl:
1835 break
1836
1837 #self._plottp(self._data)
1838
1839 if self._minmaxy is not None:
1840 self._plotter.set_limits(ylim=self._minmaxy)
1841 self._plotter.release()
1842 self._plotter.tidy()
1843 self._plotter.show(hardrefresh=False)
1844 return
Note: See TracBrowser for help on using the repository browser.