source: trunk/python/asapplotter.py@ 2539

Last change on this file since 2539 was 2538, checked in by Kana Sugimoto, 13 years ago

New Development: No

JIRA Issue: Yes (related to CAS-3749)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: added a new method, _alive(), in asaplotbase class

Test Programs: Interactive tests

Put in Release Notes: No

Module(s):

Description:

Added a new method, _alive(), in asaplotbase class. The method return True
if asaplot instance is alive. More complete check compared to 'is_dead' parameter.
asapplotter._assert_plotter method is simplified by calling this function.

Fixed misc bugs found in interactive tests.

  1. set back linewidth = 1 in plots invoked by modules, interactivemask, asapmath, and asapfitter.
  2. interactivemask module: plotter in properly quited at the end.
  3. interactivemask module: avoid error when user close interacive plot window before calling the finalization method, finish_selection().
  4. added definition of a dummy function, quit(), in asaplot class.


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.7 KB
RevLine 
[1824]1from asap.parameters import rcParams
2from asap.selector import selector
3from asap.scantable import scantable
[1862]4from asap.logging import asaplog, asaplog_post_dec
[1153]5import matplotlib.axes
[1556]6from matplotlib.font_manager import FontProperties
7from matplotlib.text import Text
[2535]8from matplotlib import _pylab_helpers
[1556]9
[1317]10import re
[203]11
[2150]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
[203]32class asapplotter:
[226]33 """
34 The ASAP plotter.
35 By default the plotter is set up to plot polarisations
36 'colour stacked' and scantables across panels.
[1858]37
38 .. note::
39
[226]40 Currenly it only plots 'spectra' not Tsys or
41 other variables.
[1858]42
[226]43 """
[1563]44 def __init__(self, visible=None , **kwargs):
[734]45 self._visible = rcParams['plotter.gui']
46 if visible is not None:
47 self._visible = visible
[2451]48 self._plotter = None
49 self._inikwg = kwargs
[710]50
[554]51 self._panelling = None
52 self._stacking = None
53 self.set_panelling()
54 self.set_stacking()
[377]55 self._rows = None
56 self._cols = None
[525]57 self._minmaxx = None
58 self._minmaxy = None
[710]59 self._datamask = None
[203]60 self._data = None
[607]61 self._lmap = None
[226]62 self._title = None
[257]63 self._ordinate = None
64 self._abcissa = None
[709]65 self._abcunit = None
[920]66 self._usermask = []
67 self._maskselection = None
68 self._selection = selector()
[1023]69 self._hist = rcParams['plotter.histogram']
[1556]70 self._fp = FontProperties()
[2037]71 self._margins = self.set_margin(refresh=False)
[1897]72 self._offset = None
[1981]73 self._startrow = 0
74 self._ipanel = -1
75 self._panelrows = []
[2053]76 self._headtext={'string': None, 'textobj': None}
[2451]77 self._colormap = None
78 self._linestyles = None
79 self._legendloc = None
[1023]80
[920]81 def _translate(self, instr):
[1910]82 keys = "s b i p t r".split()
[920]83 if isinstance(instr, str):
84 for key in keys:
85 if instr.lower().startswith(key):
86 return key
87 return None
88
[2535]89 @asaplog_post_dec
[2451]90 def _reload_plotter(self):
91 if self._plotter is not None:
[2535]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
[2451]98 if self.casabar_exists():
99 del self._plotter.figmgr.casabar
100 self._plotter.quit()
101 del self._plotter
[2535]102 asaplog.push('Loading new plotter')
[2451]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)
[710]109
[2173]110 def _new_custombar(self):
[1819]111 backend=matplotlib.get_backend()
[2168]112 if not self._visible:
113 return None
114 elif backend == "TkAgg":
[2155]115 from asap.customgui_tkagg import CustomToolbarTkAgg
[1819]116 return CustomToolbarTkAgg(self)
[2168]117 elif backend == "Qt4Agg":
118 from asap.customgui_qt4agg import CustomToolbarQT4Agg
119 return CustomToolbarQT4Agg(self)
[1995]120 return None
[1819]121
[2147]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
[2453]129 def _assert_plotter(self,action="status",errmsg=None):
[2451]130 """
[2453]131 Check plot window status. Returns True if plot window is alive.
[2451]132 Parameters
[2453]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'.
[2451]139 errmsg: An error (warning) message to send to the logger,
[2453]140 when plot window is not alive.
[2451]141 """
[2538]142 isAlive = (self._plotter is not None) and self._plotter._alive()
[2535]143 # More tests
[2538]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
[2535]156
157 if isAlive:
[2451]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
[2453]164 if action.upper().startswith("R"):
[2451]165 # reload plotter
166 self._reload_plotter()
167 return True
[2453]168 elif action.upper().startswith("H"):
[2451]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
[1862]180 @asaplog_post_dec
[935]181 def plot(self, scan=None):
[203]182 """
[920]183 Plot a scantable.
[203]184 Parameters:
[920]185 scan: a scantable
[203]186 Note:
[920]187 If a scantable was specified in a previous call
[203]188 to plot, no argument has to be given to 'replot'
[920]189 NO checking is done that the abcissas of the scantable
[203]190 are consistent e.g. all 'channel' or all 'velocity' etc.
191 """
[2451]192 if not self._data and not scan:
193 msg = "Input is not a scantable"
194 raise TypeError(msg)
[1981]195 self._startrow = 0
196 self._ipanel = -1
[2056]197 self._reset_header()
[2451]198 self._panelrows = []
199
[2453]200 self._assert_plotter(action="reload")
[2147]201 if self.casabar_exists():
[1984]202 self._plotter.figmgr.casabar.set_pagecounter(1)
[2451]203
[600]204 self._plotter.hold()
[1945]205 #self._plotter.clear()
[1897]206 if scan:
207 self.set_data(scan, refresh=False)
[2451]208 self._plotter.palette(color=0,colormap=self._colormap,
209 linestyle=0,linestyles=self._linestyles)
210 self._plotter.legend(self._legendloc)
211
[920]212 self._plot(self._data)
[709]213 if self._minmaxy is not None:
214 self._plotter.set_limits(ylim=self._minmaxy)
[2147]215 if self.casabar_exists(): self._plotter.figmgr.casabar.enable_button()
[203]216 self._plotter.release()
[1153]217 self._plotter.tidy()
218 self._plotter.show(hardrefresh=False)
[203]219 return
220
[1572]221 def gca(self):
[2451]222 errmsg = "No axis to retun. Need to plot first."
[2453]223 if not self._assert_plotter(action="status",errmsg=errmsg):
[2451]224 return None
[1572]225 return self._plotter.figure.gca()
226
[1550]227 def refresh(self):
[1572]228 """Do a soft refresh"""
[2451]229 errmsg = "No figure to re-plot. Need to plot first."
[2453]230 self._assert_plotter(action="halt",errmsg=errmsg)
[2451]231
[1550]232 self._plotter.figure.show()
233
[1555]234 def create_mask(self, nwin=1, panel=0, color=None):
[1597]235 """
[1927]236 Interactively define a mask. It retruns a mask that is equivalent to
[1597]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 """
[2451]244 ## this method relies on already plotted figure
[2453]245 if not self._assert_plotter(action="status") or (self._data is None):
[2451]246 msg = "Cannot create mask interactively on plot. Can only create mask after plotting."
247 asaplog.push( msg )
248 asaplog.post( "ERROR" )
[1555]249 return []
[1547]250 outmask = []
[1549]251 self._plotter.subplot(panel)
252 xmin, xmax = self._plotter.axes.get_xlim()
[1548]253 marg = 0.05*(xmax-xmin)
[1549]254 self._plotter.axes.set_xlim(xmin-marg, xmax+marg)
[1550]255 self.refresh()
[1695]256
[1555]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):
[1547]266 wpos = []
[1695]267 self.text(0.05,1.0, "Add start boundary",
[1555]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])
[1695]274 self.axvline(wpos[0], color=color)
[1551]275 self.text(0.05,1.0, "Add end boundary", coords="relative", fontsize=10)
[1555]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()
[1547]285 outmask.append(wpos)
[1153]286
[1555]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
[1153]293 # forwards to matplotlib axes
294 def text(self, *args, **kwargs):
[2453]295 self._assert_plotter(action="reload")
[1547]296 if kwargs.has_key("interactive"):
297 if kwargs.pop("interactive"):
298 pos = self._plotter.get_point()
299 args = tuple(pos)+args
[1153]300 self._axes_callback("text", *args, **kwargs)
[1547]301
[1358]302 text.__doc__ = matplotlib.axes.Axes.text.__doc__
[1559]303
[1153]304 def arrow(self, *args, **kwargs):
[2453]305 self._assert_plotter(action="reload")
[1547]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
[1153]313 self._axes_callback("arrow", *args, **kwargs)
[1547]314
[1358]315 arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
[1559]316
317 def annotate(self, text, xy=None, xytext=None, **kwargs):
[2453]318 self._assert_plotter(action="reload")
[1559]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
[1153]329 def axvline(self, *args, **kwargs):
[2453]330 self._assert_plotter(action="reload")
[1547]331 if kwargs.has_key("interactive"):
332 if kwargs.pop("interactive"):
333 pos = self._plotter.get_point()
334 args = (pos[0],)+args
[1153]335 self._axes_callback("axvline", *args, **kwargs)
[1559]336
[1358]337 axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
[1547]338
[1153]339 def axhline(self, *args, **kwargs):
[2453]340 self._assert_plotter(action="reload")
[1547]341 if kwargs.has_key("interactive"):
342 if kwargs.pop("interactive"):
343 pos = self._plotter.get_point()
344 args = (pos[1],)+args
[1153]345 self._axes_callback("axhline", *args, **kwargs)
[1559]346
[1358]347 axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
[1547]348
[1153]349 def axvspan(self, *args, **kwargs):
[2453]350 self._assert_plotter(action="reload")
[1547]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
[1153]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???
[1547]360 #del self._plotter.axes.patches[-1]
361
[1358]362 axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
[1232]363
[1153]364 def axhspan(self, *args, **kwargs):
[2453]365 self._assert_plotter(action="reload")
[1547]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
[1232]371 self._axes_callback("axhspan", *args, **kwargs)
[1153]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???
[1547]375 #del self._plotter.axes.patches[-1]
[1559]376
[1358]377 axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
[1153]378
379 def _axes_callback(self, axesfunc, *args, **kwargs):
[2453]380 self._assert_plotter(action="reload")
[1153]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
[1862]398 @asaplog_post_dec
[1819]399 def set_data(self, scan, refresh=True):
400 """
[1824]401 Set a scantable to plot.
[1819]402 Parameters:
403 scan: a scantable
404 refresh: True (default) or False. If True, the plot is
[1824]405 replotted based on the new parameter setting(s).
[1819]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
[1824]410 setting data selections (set_selection) and/or masks (set_mask).
[1819]411 """
412 from asap import scantable
413 if isinstance(scan, scantable):
414 if self._data is not None:
415 if scan != self._data:
[2123]416 del self._data
[1819]417 self._data = scan
418 # reset
419 self._reset()
[1897]420 msg = "A new scantable is set to the plotter. "\
421 "The masks and data selections are reset."
[1819]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)
[1547]429
[1819]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
[1862]438 @asaplog_post_dec
[1819]439 def set_mode(self, stacking=None, panelling=None, refresh=True):
[203]440 """
[377]441 Set the plots look and feel, i.e. what you want to see on the plot.
[203]442 Parameters:
443 stacking: tell the plotter which variable to plot
[1217]444 as line colour overlays (default 'pol')
[203]445 panelling: tell the plotter which variable to plot
446 across multiple panels (default 'scan'
[1819]447 refresh: True (default) or False. If True, the plot is
[1824]448 replotted based on the new parameter setting(s).
[1819]449 Otherwise,the parameter(s) are set without replotting.
[203]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
[1989]457 'row' 'Row' 'r': Rows
458 When either 'stacking' or 'panelling' is set to 'row',
459 the other parameter setting is ignored.
[203]460 """
[753]461 msg = "Invalid mode"
462 if not self.set_panelling(panelling) or \
463 not self.set_stacking(stacking):
[1859]464 raise TypeError(msg)
[1989]465 #if self._panelling == 'r':
466 # self._stacking = '_r'
467 #if self._stacking == 'r':
468 # self._panelling = '_r'
[1819]469 if refresh and self._data: self.plot(self._data)
[203]470 return
471
[554]472 def set_panelling(self, what=None):
[1858]473 """Set the 'panelling' mode i.e. which type of spectra should be
474 spread across different panels.
475 """
476
[554]477 mode = what
478 if mode is None:
479 mode = rcParams['plotter.panelling']
480 md = self._translate(mode)
[203]481 if md:
[554]482 self._panelling = md
[226]483 self._title = None
[1989]484 #if md == 'r':
485 # self._stacking = '_r'
[1981]486 # you need to reset counters for multi page plotting
487 self._reset_counters()
[203]488 return True
489 return False
490
[1819]491 def set_layout(self,rows=None,cols=None,refresh=True):
[377]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
[1819]498 refresh: True (default) or False. If True, the plot is
[1824]499 replotted based on the new parameter setting(s).
[1819]500 Otherwise,the parameter(s) are set without replotting.
[377]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
[1819]507 if refresh and self._data: self.plot(self._data)
[377]508 return
509
[709]510 def set_stacking(self, what=None):
[1858]511 """Set the 'stacking' mode i.e. which type of spectra should be
512 overlayed.
513 """
[554]514 mode = what
[709]515 if mode is None:
516 mode = rcParams['plotter.stacking']
[554]517 md = self._translate(mode)
[203]518 if md:
519 self._stacking = md
[226]520 self._lmap = None
[1989]521 #if md == 'r':
522 # self._panelling = '_r'
[1981]523 # you need to reset counters for multi page plotting
524 self._reset_counters()
[203]525 return True
526 return False
527
[1981]528 def _reset_counters(self):
529 self._startrow = 0
530 self._ipanel = -1
531 self._panelrows = []
532
[1897]533 def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True, offset=None):
[203]534 """
535 Set the range of interest on the abcissa of the plot
536 Parameters:
[525]537 [x,y]start,[x,y]end: The start and end points of the 'zoom' window
[1819]538 refresh: True (default) or False. If True, the plot is
[1824]539 replotted based on the new parameter setting(s).
[1819]540 Otherwise,the parameter(s) are set without replotting.
[1897]541 offset: shift the abcissa by the given amount. The abcissa label will
542 have '(relative)' appended to it.
[203]543 Note:
544 These become non-sensical when the unit changes.
545 use plotter.set_range() without parameters to reset
546
547 """
[1897]548 self._offset = offset
[525]549 if xstart is None and xend is None:
550 self._minmaxx = None
[600]551 else:
552 self._minmaxx = [xstart,xend]
[525]553 if ystart is None and yend is None:
554 self._minmaxy = None
[600]555 else:
[709]556 self._minmaxy = [ystart,yend]
[1819]557 if refresh and self._data: self.plot(self._data)
[203]558 return
[709]559
[1819]560 def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
[203]561 """
562 Specify a mapping for the legend instead of using the default
563 indices:
564 Parameters:
[1101]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:
[1096]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
[1819]584 refresh: True (default) or False. If True, the plot is
[1824]585 replotted based on the new parameter setting(s).
[1819]586 Otherwise,the parameter(s) are set without replotting.
[203]587
588 Example:
[485]589 If the data has two IFs/rest frequencies with index 0 and 1
[203]590 for CO and SiO:
591 plotter.set_stacking('i')
[710]592 plotter.set_legend(['CO','SiO'])
[203]593 plotter.plot()
[710]594 plotter.set_legend([r'$^{12}CO$', r'SiO'])
[203]595 """
596 self._lmap = mp
[2451]597 #self._plotter.legend(mode)
598 self._legendloc = mode
[1101]599 if isinstance(fontsize, int):
600 from matplotlib import rc as rcp
601 rcp('legend', fontsize=fontsize)
[1819]602 if refresh and self._data: self.plot(self._data)
[226]603 return
604
[1819]605 def set_title(self, title=None, fontsize=None, refresh=True):
[710]606 """
[2451]607 Set the title of sub-plots. If multiple sub-plots are plotted,
[710]608 multiple titles have to be specified.
[1819]609 Parameters:
[2451]610 title: a list of titles of sub-plots.
611 fontsize: a font size of titles (integer)
[1819]612 refresh: True (default) or False. If True, the plot is
[1824]613 replotted based on the new parameter setting(s).
[1819]614 Otherwise,the parameter(s) are set without replotting.
[710]615 Example:
616 # two panels are visible on the plotter
[2451]617 plotter.set_title(['First Panel','Second Panel'])
[710]618 """
[226]619 self._title = title
[1101]620 if isinstance(fontsize, int):
621 from matplotlib import rc as rcp
622 rcp('axes', titlesize=fontsize)
[1819]623 if refresh and self._data: self.plot(self._data)
[226]624 return
625
[1819]626 def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
[710]627 """
628 Set the y-axis label of the plot. If multiple panels are plotted,
629 multiple labels have to be specified.
[1021]630 Parameters:
631 ordinate: a list of ordinate labels. None (default) let
632 data determine the labels
[2451]633 fontsize: a font size of vertical axis labels (integer)
[1819]634 refresh: True (default) or False. If True, the plot is
[1824]635 replotted based on the new parameter setting(s).
[1819]636 Otherwise,the parameter(s) are set without replotting.
[710]637 Example:
638 # two panels are visible on the plotter
[2451]639 plotter.set_ordinate(['First Y-Axis','Second Y-Axis'])
[710]640 """
[257]641 self._ordinate = ordinate
[1101]642 if isinstance(fontsize, int):
643 from matplotlib import rc as rcp
644 rcp('axes', labelsize=fontsize)
645 rcp('ytick', labelsize=fontsize)
[1819]646 if refresh and self._data: self.plot(self._data)
[257]647 return
648
[1819]649 def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
[710]650 """
651 Set the x-axis label of the plot. If multiple panels are plotted,
652 multiple labels have to be specified.
[1021]653 Parameters:
654 abcissa: a list of abcissa labels. None (default) let
655 data determine the labels
[2451]656 fontsize: a font size of horizontal axis labels (integer)
[1819]657 refresh: True (default) or False. If True, the plot is
[1824]658 replotted based on the new parameter setting(s).
[1819]659 Otherwise,the parameter(s) are set without replotting.
[710]660 Example:
661 # two panels are visible on the plotter
[2451]662 plotter.set_ordinate(['First X-Axis','Second X-Axis'])
[710]663 """
[257]664 self._abcissa = abcissa
[1101]665 if isinstance(fontsize, int):
666 from matplotlib import rc as rcp
667 rcp('axes', labelsize=fontsize)
668 rcp('xtick', labelsize=fontsize)
[1819]669 if refresh and self._data: self.plot(self._data)
[257]670 return
671
[1819]672 def set_colors(self, colmap, refresh=True):
[377]673 """
[1217]674 Set the colours to be used. The plotter will cycle through
675 these colours when lines are overlaid (stacking mode).
[1021]676 Parameters:
[1217]677 colmap: a list of colour names
[1819]678 refresh: True (default) or False. If True, the plot is
[1824]679 replotted based on the new parameter setting(s).
[1819]680 Otherwise,the parameter(s) are set without replotting.
[710]681 Example:
[2451]682 plotter.set_colors('red green blue')
[710]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 """
[2451]687 #if isinstance(colmap,str):
688 # colmap = colmap.split()
689 #self._plotter.palette(0, colormap=colmap)
690 self._colormap = colmap
[1819]691 if refresh and self._data: self.plot(self._data)
[710]692
[1217]693 # alias for english speakers
694 set_colours = set_colors
695
[1819]696 def set_histogram(self, hist=True, linewidth=None, refresh=True):
[1021]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
[2451]703 linewidth: a line width
[1819]704 refresh: True (default) or False. If True, the plot is
[1824]705 replotted based on the new parameter setting(s).
[1819]706 Otherwise,the parameter(s) are set without replotting.
[1021]707 """
[1023]708 self._hist = hist
[1101]709 if isinstance(linewidth, float) or isinstance(linewidth, int):
710 from matplotlib import rc as rcp
711 rcp('lines', linewidth=linewidth)
[1819]712 if refresh and self._data: self.plot(self._data)
[1023]713
[1819]714 def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
[710]715 """
[734]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.
[710]719 Parameters:
[2451]720 linestyles: a list of linestyles to use.
[710]721 'line', 'dashed', 'dotted', 'dashdot',
722 'dashdotdot' and 'dashdashdot' are
723 possible
[2451]724 linewidth: a line width
[1819]725 refresh: True (default) or False. If True, the plot is
[1824]726 replotted based on the new parameter setting(s).
[1819]727 Otherwise,the parameter(s) are set without replotting.
[710]728 Example:
[2451]729 plotter.set_colors('black')
730 plotter.set_linestyles('line dashed dotted dashdot')
[710]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 """
[2451]735 #if isinstance(linestyles,str):
736 # linestyles = linestyles.split()
737 #self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
738 self._linestyles = linestyles
[1101]739 if isinstance(linewidth, float) or isinstance(linewidth, int):
740 from matplotlib import rc as rcp
741 rcp('lines', linewidth=linewidth)
[1819]742 if refresh and self._data: self.plot(self._data)
[710]743
[1819]744 def set_font(self, refresh=True,**kwargs):
[1101]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
[1819]753 refresh: True (default) or False. If True, the plot is
[1824]754 replotted based on the new parameter setting(s).
[1819]755 Otherwise,the parameter(s) are set without replotting.
[1101]756 """
757 from matplotlib import rc as rcp
[1547]758 fdict = {}
759 for k,v in kwargs.iteritems():
760 if v:
761 fdict[k] = v
[1556]762 self._fp = FontProperties(**fdict)
[1819]763 if refresh and self._data: self.plot(self._data)
[1101]764
[2037]765 def set_margin(self,margin=[],refresh=True):
[1819]766 """
[2037]767 Set margins between subplots and plot edges.
[1819]768 Parameters:
[2037]769 margin: a list of margins in figure coordinate (0-1),
[1824]770 i.e., fraction of the figure width or height.
[1819]771 The order of elements should be:
772 [left, bottom, right, top, horizontal space btw panels,
[1824]773 vertical space btw panels].
[1819]774 refresh: True (default) or False. If True, the plot is
[1824]775 replotted based on the new parameter setting(s).
[1819]776 Otherwise,the parameter(s) are set without replotting.
777 Note
[2037]778 * When margin is not specified, the values are reset to the defaults
[1819]779 of matplotlib.
[1824]780 * If any element is set to be None, the current value is adopted.
[1819]781 """
[2037]782 if margin == []: self._margins=self._reset_margin()
[1824]783 else:
[2037]784 self._margins=[None]*6
785 self._margins[0:len(margin)]=margin
786 #print "panel margin set to ",self._margins
[1819]787 if refresh and self._data: self.plot(self._data)
788
[2037]789 def _reset_margin(self):
[1819]790 ks=map(lambda x: 'figure.subplot.'+x,
791 ['left','bottom','right','top','hspace','wspace'])
792 return map(matplotlib.rcParams.get,ks)
793
[1259]794 def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
[1146]795 location=None):
796 """
[1158]797 Plot a line catalog.
798 Parameters:
799 linecat: the linecatalog to plot
[1168]800 doppler: the velocity shift to apply to the frequencies
[1158]801 deltachan: the number of channels to include each side of the
802 line to determine a local maximum/minimum
[1927]803 rotate: the rotation (in degrees) for the text label (default 90.0)
[1158]804 location: the location of the line annotation from the 'top',
805 'bottom' or alternate (None - the default)
[1165]806 Notes:
807 If the spectrum is flagged no line will be drawn in that location.
[1146]808 """
[2451]809 errmsg = "Cannot plot spectral lines. Need to plot scantable first."
[2453]810 self._assert_plotter(action="halt",errmsg=errmsg)
[1259]811 if not self._data:
812 raise RuntimeError("No scantable has been plotted yet.")
[1146]813 from asap._asap import linecatalog
[1259]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.")
[1739]818 from numpy import ma
[1146]819 for j in range(len(self._plotter.subplots)):
820 self._plotter.subplot(j)
821 lims = self._plotter.axes.get_xlim()
[1153]822 for row in range(linecat.nrow()):
[1259]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()]
[1165]826 c = 299792.458
[1174]827 freq = restf*(1.0-doppler/c)
[1146]828 if lims[0] < freq < lims[1]:
829 if location is None:
830 loc = 'bottom'
[1153]831 if row%2: loc='top'
[1146]832 else: loc = location
[1153]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)
[1167]857 y = line._y[s]
[1165]858 maxy = ma.maximum(y)
859 if isinstance( maxy, float):
860 maxys.append(maxy)
[1164]861 if len(maxys):
862 peak = max(maxys)
[1165]863 if peak > self._plotter.axes.get_ylim()[1]:
864 loc = 'bottom'
[1164]865 else:
866 continue
[1157]867 self._plotter.vline_with_label(freq, peak,
868 linecat.get_name(row),
869 location=loc, rotate=rotate)
[1153]870 self._plotter.show(hardrefresh=False)
[1146]871
[1153]872
[710]873 def save(self, filename=None, orientation=None, dpi=None):
874 """
[1927]875 Save the plot to a file. The known formats are 'png', 'ps', 'eps'.
[377]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.
[709]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.
[710]886 dpi: The dpi of the output non-ps plot
[377]887 """
[2451]888 errmsg = "Cannot save figure. Need to plot first."
[2453]889 self._assert_plotter(action="halt",errmsg=errmsg)
[2451]890
[709]891 self._plotter.save(filename,orientation,dpi)
[377]892 return
[709]893
[1862]894 @asaplog_post_dec
[1819]895 def set_mask(self, mask=None, selection=None, refresh=True):
[525]896 """
[734]897 Set a plotting mask for a specific polarization.
[2451]898 This is useful for masking out 'noise' Pangle outside a source.
[734]899 Parameters:
[920]900 mask: a mask from scantable.create_mask
901 selection: the spectra to apply the mask to.
[1819]902 refresh: True (default) or False. If True, the plot is
[1824]903 replotted based on the new parameter setting(s).
[1819]904 Otherwise,the parameter(s) are set without replotting.
[734]905 Example:
[920]906 select = selector()
[2451]907 select.setpolstrings('Pangle')
[920]908 plotter.set_mask(mymask, select)
[734]909 """
[710]910 if not self._data:
[920]911 msg = "Can only set mask after a first call to plot()"
[1859]912 raise RuntimeError(msg)
[920]913 if len(mask):
914 if isinstance(mask, list) or isinstance(mask, tuple):
915 self._usermask = array(mask)
[710]916 else:
[920]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):
[947]922 self._maskselection = {'b': selection.get_beams(),
923 's': selection.get_scans(),
924 'i': selection.get_ifs(),
925 'p': selection.get_pols(),
[920]926 't': [] }
[710]927 else:
[920]928 self._maskselection = None
[1819]929 if refresh: self.plot(self._data)
[710]930
[709]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
[1819]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
[709]949 # find max index
[1819]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
[709]959 if start > end:
[1819]960 return end,start+1
961 elif start < end:
962 return start,end+1
963 else:
964 return start,end
[709]965
[710]966 def _reset(self):
[920]967 self._usermask = []
[710]968 self._usermaskspectra = None
[1897]969 self._offset = None
[920]970 self.set_selection(None, False)
[2051]971 self._reset_header()
[920]972
[2051]973 def _reset_header(self):
[2053]974 self._headtext={'string': None, 'textobj': None}
[2051]975
[920]976 def _plot(self, scan):
[947]977 savesel = scan.get_selection()
978 sel = savesel + self._selection
[1910]979 order = self._get_sortstring([self._panelling,self._stacking])
980 if order:
981 sel.set_order(order)
[947]982 scan.set_selection(sel)
[920]983 d = {'b': scan.getbeam, 's': scan.getscan,
[1949]984 'i': scan.getif, 'p': scan.getpol, 't': scan.get_time,
[1989]985 'r': int}#, '_r': int}
[920]986
[1148]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
[1175]993 else: n = len(n0)
[1148]994 if isinstance(nstack0, int): nstack = nstack0
[1175]995 else: nstack = len(nstack0)
[1989]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'
[1913]1002 nptot = n
[1582]1003 maxpanel, maxstack = 16,16
[1913]1004 if nstack > maxstack:
1005 msg ="Scan to be overlayed contains more than %d selections.\n" \
1006 "Selecting first %d selections..." % (maxstack, maxstack)
[920]1007 asaplog.push(msg)
[1861]1008 asaplog.post('WARN')
[998]1009 nstack = min(nstack,maxstack)
[2038]1010 #n = min(n-self._ipanel-1,maxpanel)
1011 n = n-self._ipanel-1
[2011]1012
1013 ganged = False
[920]1014 if n > 1:
1015 ganged = rcParams['plotter.ganged']
[1819]1016 if self._panelling == 'i':
1017 ganged = False
[920]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,
[2277]1021 nplots=n,margin=self._margins,ganged=ganged)
[920]1022 else:
[2038]1023 n = min(n,maxpanel)
[2277]1024 self._plotter.set_panels(rows=n,cols=0,nplots=n,margin=self._margins,ganged=ganged)
[920]1025 else:
[2037]1026 self._plotter.set_panels(margin=self._margins)
[1913]1027 #r = 0
[1981]1028 r = self._startrow
[920]1029 nr = scan.nrow()
1030 a0,b0 = -1,-1
1031 allxlim = []
[1018]1032 allylim = []
[1981]1033 #newpanel=True
1034 newpanel=False
[920]1035 panelcount,stackcount = 0,0
[1981]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
[1002]1044 while r < nr:
[920]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()
[1897]1054 if self._offset and not self._abcissa:
1055 xlab += " (relative)"
[920]1056 ylab = self._ordinate and self._ordinate[panelcount] \
1057 or scan._get_ordinate_label()
[1547]1058 self._plotter.set_axes('xlabel', xlab)
1059 self._plotter.set_axes('ylabel', ylab)
[1989]1060 #lbl = self._get_label(scan, r, self._panelling, self._title)
1061 lbl = self._get_label(scan, r, titlemode, self._title)
[920]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
[1989]1067 #lbl = self._get_label(scan, r, self._panelling, None)
1068 lbl = self._get_label(scan, r, titlemode, None)
[920]1069 self._plotter.set_axes('title',lbl)
1070 newpanel = True
[1913]1071 stackcount = 0
[920]1072 panelcount += 1
[1981]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
[1944]1078 #if (b > b0 or newpanel) and stackcount < nstack:
[2277]1079 if stackcount < nstack and (newpanel or rowstack or (a == a0 and b > b0)):
[920]1080 y = []
1081 if len(polmodes):
1082 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
1083 else:
1084 y = scan._getspectrum(r)
[1995]1085 # flag application
1086 mr = scan._getflagrow(r)
[1739]1087 from numpy import ma, array
[1995]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):
[2277]1094 if d[self._stacking](r) in self._maskselection[self._stacking]:
[1995]1095 m = logical_and(m, self._usermask)
[2277]1096 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
[1995]1097
[1897]1098 x = array(scan._getabcissa(r))
1099 if self._offset:
1100 x += self._offset
[920]1101 if self._minmaxx is not None:
1102 s,e = self._slice_indeces(x)
1103 x = x[s:e]
1104 y = y[s:e]
[1096]1105 if len(x) > 1024 and rcParams['plotter.decimate']:
1106 fac = len(x)/1024
[920]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)
[1023]1118 plotit = self._plotter.plot
1119 if self._hist: plotit = self._plotter.hist
[1995]1120 if len(x) > 0 and not mr:
[1146]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
[1819]1126 else:
1127 xlim = self._minmaxx or []
1128 allxlim += xlim
1129 ylim= self._minmaxy or []
1130 allylim += ylim
[920]1131 stackcount += 1
[1981]1132 a0=a
1133 b0=b
[920]1134 # last in colour stack -> autoscale x
[1819]1135 if stackcount == nstack and len(allxlim) > 0:
[920]1136 allxlim.sort()
[1819]1137 self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
[1989]1138 if ganged:
1139 allxlim = [allxlim[0],allxlim[-1]]
1140 else:
1141 # clear
1142 allxlim =[]
[920]1143
1144 newpanel = False
[1981]1145 #a0=a
1146 #b0=b
[920]1147 # ignore following rows
[1981]1148 if (panelcount == n and stackcount == nstack) or (r == nr-1):
[1018]1149 # last panel -> autoscale y if ganged
[1989]1150 #if rcParams['plotter.ganged'] and len(allylim) > 0:
1151 if ganged and len(allylim) > 0:
[1018]1152 allylim.sort()
1153 self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
[998]1154 break
[920]1155 r+=1 # next row
[1981]1156
1157 # save the current counter for multi-page plotting
1158 self._startrow = r+1
1159 self._ipanel += panelcount
[2147]1160 if self.casabar_exists():
[1981]1161 if self._ipanel >= nptot-1:
[1913]1162 self._plotter.figmgr.casabar.disable_next()
1163 else:
1164 self._plotter.figmgr.casabar.enable_next()
[1981]1165 if self._ipanel + 1 - panelcount > 0:
1166 self._plotter.figmgr.casabar.enable_prev()
1167 else:
1168 self._plotter.figmgr.casabar.disable_prev()
1169
[947]1170 #reset the selector to the scantable's original
1171 scan.set_selection(savesel)
[1824]1172
[1819]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):
[1556]1176 for o in self._plotter.figure.findobj(Text):
1177 o.set_fontproperties(self._fp)
[920]1178
[1910]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 }
[1944]1182 if not (type(lorders) == list) and not (type(lorders) == tuple):
[1910]1183 return None
1184 if len(lorders) > 0:
1185 lsorts = []
1186 for order in lorders:
[1989]1187 if order == "r":
1188 # don't sort if row panelling/stacking
1189 return None
[1910]1190 ssort = d0[order]
1191 if ssort:
1192 lsorts.append(ssort)
1193 return lsorts
1194 return None
1195
[1582]1196 def set_selection(self, selection=None, refresh=True, **kw):
[1819]1197 """
1198 Parameters:
1199 selection: a selector object (default unset the selection)
1200 refresh: True (default) or False. If True, the plot is
[1824]1201 replotted based on the new parameter setting(s).
[1819]1202 Otherwise,the parameter(s) are set without replotting.
1203 """
[1582]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
[1910]1219 order = self._get_sortstring([self._panelling,self._stacking])
1220 if order:
1221 self._selection.set_order(order)
[1819]1222 if refresh and self._data: self.plot(self._data)
[920]1223
1224 def _get_selected_n(self, scan):
[1148]1225 d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
[1910]1226 'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle,
[1989]1227 'r': scan.nrow}#, '_r': False}
[1148]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(),
[1910]1232 't': self._selection.get_cycles(),
[1989]1233 'r': False}#, '_r': 1}
[920]1234 n = d2[self._panelling] or d1[self._panelling]()
1235 nstack = d2[self._stacking] or d1[self._stacking]()
[1989]1236 # handle row panelling/stacking
1237 if self._panelling == 'r':
1238 nstack = 1
1239 elif self._stacking == 'r':
1240 n = 1
[920]1241 return n,nstack
1242
1243 def _get_label(self, scan, row, mode, userlabel=None):
[1153]1244 if isinstance(userlabel, list) and len(userlabel) == 0:
1245 userlabel = " "
[947]1246 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
[920]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)),
[1819]1252 #'s': scan._getsourcename(row),
1253 's': "Scan "+str(scan.getscan(row))+\
1254 " ("+str(scan._getsourcename(row))+")",
[920]1255 'i': "IF"+str(scan.getif(row)),
[964]1256 'p': poleval,
[1910]1257 't': str(scan.get_time(row)),
1258 'r': "row "+str(row),
[1913]1259 #'_r': str(scan.get_time(row))+",\nIF"+str(scan.getif(row))+", "+poleval+", Beam"+str(scan.getbeam(row)) }
1260 '_r': "" }
[920]1261 return userlabel or d[mode]
[1153]1262
[1819]1263 def plotazel(self, scan=None, outfile=None):
[1391]1264 """
[1696]1265 plot azimuth and elevation versus time of a scantable
[1391]1266 """
[1923]1267 visible = rcParams['plotter.gui']
[1696]1268 from matplotlib import pylab as PL
1269 from matplotlib.dates import DateFormatter, timezone
1270 from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
[1391]1271 from matplotlib.ticker import MultipleLocator
[1739]1272 from numpy import array, pi
[1923]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)
[1819]1277 self._data = scan
1278 self._outfile = outfile
[1556]1279 dates = self._data.get_time(asdatetime=True)
[1391]1280 t = PL.date2num(dates)
1281 tz = timezone('UTC')
1282 PL.cla()
1283 PL.ioff()
1284 PL.clf()
[2037]1285 # Adjust subplot margins
1286 if len(self._margins) != 6:
1287 self.set_margin(refresh=False)
1288 lef, bot, rig, top, wsp, hsp = self._margins
[1819]1289 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1290 wspace=wsp,hspace=hsp)
[1824]1291
[1391]1292 tdel = max(t) - min(t)
1293 ax = PL.subplot(2,1,1)
1294 el = array(self._data.get_elevation())*180./pi
1295 PL.ylabel('El [deg.]')
1296 dstr = dates[0].strftime('%Y/%m/%d')
1297 if tdel > 1.0:
1298 dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
1299 dstr = dstr + " - " + dstr2
1300 majloc = DayLocator()
1301 minloc = HourLocator(range(0,23,12))
1302 timefmt = DateFormatter("%b%d")
[1696]1303 elif tdel > 24./60.:
1304 timefmt = DateFormatter('%H:%M')
1305 majloc = HourLocator()
1306 minloc = MinuteLocator(30)
[1391]1307 else:
[1696]1308 timefmt = DateFormatter('%H:%M')
1309 majloc = MinuteLocator(interval=5)
1310 minloc = SecondLocator(30)
1311
[1391]1312 PL.title(dstr)
[1819]1313 if tdel == 0.0:
1314 th = (t - PL.floor(t))*24.0
1315 PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
1316 else:
1317 PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
1318 #ax.grid(True)
1319 ax.xaxis.set_major_formatter(timefmt)
1320 ax.xaxis.set_major_locator(majloc)
1321 ax.xaxis.set_minor_locator(minloc)
[1391]1322 ax.yaxis.grid(True)
[1819]1323 yloc = MultipleLocator(30)
1324 ax.set_ylim(0,90)
1325 ax.yaxis.set_major_locator(yloc)
[1391]1326 if tdel > 1.0:
1327 labels = ax.get_xticklabels()
1328 # PL.setp(labels, fontsize=10, rotation=45)
1329 PL.setp(labels, fontsize=10)
[1819]1330
[1391]1331 # Az plot
1332 az = array(self._data.get_azimuth())*180./pi
1333 if min(az) < 0:
1334 for irow in range(len(az)):
1335 if az[irow] < 0: az[irow] += 360.0
1336
[1819]1337 ax2 = PL.subplot(2,1,2)
1338 #PL.xlabel('Time (UT [hour])')
1339 PL.ylabel('Az [deg.]')
1340 if tdel == 0.0:
1341 PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
1342 else:
1343 PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
1344 ax2.xaxis.set_major_formatter(timefmt)
1345 ax2.xaxis.set_major_locator(majloc)
1346 ax2.xaxis.set_minor_locator(minloc)
1347 #ax2.grid(True)
1348 ax2.set_ylim(0,360)
[1696]1349 ax2.yaxis.grid(True)
[1819]1350 #hfmt = DateFormatter('%H')
1351 #hloc = HourLocator()
1352 yloc = MultipleLocator(60)
1353 ax2.yaxis.set_major_locator(yloc)
1354 if tdel > 1.0:
1355 labels = ax2.get_xticklabels()
1356 PL.setp(labels, fontsize=10)
1357 PL.xlabel('Time (UT [day])')
1358 else:
1359 PL.xlabel('Time (UT [hour])')
1360
[1391]1361 PL.ion()
1362 PL.draw()
[2416]1363 if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
[1819]1364 if (self._outfile is not None):
1365 PL.savefig(self._outfile)
[1391]1366
[1819]1367 def plotpointing(self, scan=None, outfile=None):
[1391]1368 """
1369 plot telescope pointings
1370 """
[1923]1371 visible = rcParams['plotter.gui']
[1696]1372 from matplotlib import pylab as PL
[1819]1373 from numpy import array, pi
[1923]1374 if not visible or not self._visible:
1375 PL.ioff()
1376 from matplotlib.backends.backend_agg import FigureCanvasAgg
1377 PL.gcf().canvas.switch_backends(FigureCanvasAgg)
[1819]1378 self._data = scan
1379 self._outfile = outfile
[1391]1380 dir = array(self._data.get_directionval()).transpose()
1381 ra = dir[0]*180./pi
1382 dec = dir[1]*180./pi
1383 PL.cla()
[1819]1384 #PL.ioff()
[1391]1385 PL.clf()
[2037]1386 # Adjust subplot margins
1387 if len(self._margins) != 6:
1388 self.set_margin(refresh=False)
1389 lef, bot, rig, top, wsp, hsp = self._margins
[1819]1390 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
1391 wspace=wsp,hspace=hsp)
1392 ax = PL.gca()
1393 #ax = PL.axes([0.1,0.1,0.8,0.8])
1394 #ax = PL.axes([0.1,0.1,0.8,0.8])
[1391]1395 ax.set_aspect('equal')
[1696]1396 PL.plot(ra, dec, 'b,')
[1391]1397 PL.xlabel('RA [deg.]')
1398 PL.ylabel('Declination [deg.]')
1399 PL.title('Telescope pointings')
1400 [xmin,xmax,ymin,ymax] = PL.axis()
1401 PL.axis([xmax,xmin,ymin,ymax])
[2416]1402 PL.ion()
[1391]1403 PL.draw()
[2416]1404 if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
[1819]1405 if (self._outfile is not None):
1406 PL.savefig(self._outfile)
1407
1408 # plot total power data
1409 # plotting in time is not yet implemented..
[1862]1410 @asaplog_post_dec
[1819]1411 def plottp(self, scan=None, outfile=None):
[2453]1412 self._assert_plotter(action="reload")
[1819]1413 self._plotter.hold()
1414 self._plotter.clear()
1415 from asap import scantable
1416 if not self._data and not scan:
1417 msg = "Input is not a scantable"
1418 raise TypeError(msg)
1419 if isinstance(scan, scantable):
1420 if self._data is not None:
1421 if scan != self._data:
1422 self._data = scan
1423 # reset
1424 self._reset()
1425 else:
1426 self._data = scan
1427 self._reset()
1428 # ranges become invalid when abcissa changes?
1429 #if self._abcunit and self._abcunit != self._data.get_unit():
1430 # self._minmaxx = None
1431 # self._minmaxy = None
1432 # self._abcunit = self._data.get_unit()
1433 # self._datamask = None
1434
[2037]1435 # Adjust subplot margins
1436 if len(self._margins) !=6: self.set_margin(refresh=False)
1437 lef, bot, rig, top, wsp, hsp = self._margins
[1819]1438 self._plotter.figure.subplots_adjust(
1439 left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
[2147]1440 if self.casabar_exists(): self._plotter.figmgr.casabar.disable_button()
[1819]1441 self._plottp(self._data)
1442 if self._minmaxy is not None:
1443 self._plotter.set_limits(ylim=self._minmaxy)
1444 self._plotter.release()
1445 self._plotter.tidy()
1446 self._plotter.show(hardrefresh=False)
1447 return
1448
1449 def _plottp(self,scan):
1450 """
1451 private method for plotting total power data
1452 """
1453 from numpy import ma, array, arange, logical_not
1454 r=0
1455 nr = scan.nrow()
1456 a0,b0 = -1,-1
1457 allxlim = []
1458 allylim = []
1459 y=[]
1460 self._plotter.set_panels()
1461 self._plotter.palette(0)
1462 #title
1463 #xlab = self._abcissa and self._abcissa[panelcount] \
1464 # or scan._getabcissalabel()
1465 #ylab = self._ordinate and self._ordinate[panelcount] \
1466 # or scan._get_ordinate_label()
1467 xlab = self._abcissa or 'row number' #or Time
1468 ylab = self._ordinate or scan._get_ordinate_label()
1469 self._plotter.set_axes('xlabel',xlab)
1470 self._plotter.set_axes('ylabel',ylab)
1471 lbl = self._get_label(scan, r, 's', self._title)
1472 if isinstance(lbl, list) or isinstance(lbl, tuple):
1473 # if 0 <= panelcount < len(lbl):
1474 # lbl = lbl[panelcount]
1475 # else:
1476 # get default label
1477 lbl = self._get_label(scan, r, self._panelling, None)
1478 self._plotter.set_axes('title',lbl)
1479 y=array(scan._get_column(scan._getspectrum,-1))
1480 m = array(scan._get_column(scan._getmask,-1))
1481 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1482 x = arange(len(y))
1483 # try to handle spectral data somewhat...
1484 l,m = y.shape
1485 if m > 1:
1486 y=y.mean(axis=1)
1487 plotit = self._plotter.plot
1488 llbl = self._get_label(scan, r, self._stacking, None)
1489 self._plotter.set_line(label=llbl)
1490 if len(x) > 0:
1491 plotit(x,y)
1492
1493
1494 # forwards to matplotlib.Figure.text
1495 def figtext(self, *args, **kwargs):
1496 """
1497 Add text to figure at location x,y (relative 0-1 coords).
1498 This method forwards *args and **kwargs to a Matplotlib method,
1499 matplotlib.Figure.text.
1500 See the method help for detailed information.
1501 """
[2453]1502 self._assert_plotter(action="reload")
[1819]1503 self._plotter.text(*args, **kwargs)
1504 # end matplotlib.Figure.text forwarding function
1505
1506
1507 # printing header information
[1862]1508 @asaplog_post_dec
[2053]1509 def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
[1819]1510 """
1511 print data (scantable) header on the plot and/or logger.
[2056]1512 To plot the header on the plot, this method should be called after
1513 plotting spectra by the method, asapplotter.plot.
[1819]1514 Parameters:
[1824]1515 plot: whether or not print header info on the plot.
[2053]1516 fontsize: header font size (valid only plot=True)
[1819]1517 logger: whether or not print header info on the logger.
1518 selstr: additional selection string (not verified)
[2053]1519 extrastr: additional string to print at the beginning (not verified)
[1819]1520 """
[1859]1521 if not plot and not logger:
1522 return
1523 if not self._data:
1524 raise RuntimeError("No scantable has been set yet.")
[1824]1525 # Now header will be printed on plot and/or logger.
1526 # Get header information and format it.
[2112]1527 ssum=self._data._list_header()
[1819]1528 # Print Observation header to the upper-left corner of plot
[2290]1529 headstr=[ssum[0:ssum.find('Obs. Type:')]]
1530 headstr.append(ssum[ssum.find('Obs. Type:'):ssum.find('Flux Unit:')])
[2053]1531 if extrastr != '':
1532 headstr[0]=extrastr+'\n'+headstr[0]
1533 self._headtext['extrastr'] = extrastr
[2112]1534 if selstr != '':
1535 selstr += '\n'
1536 self._headtext['selstr'] = selstr
[2056]1537 ssel=(selstr+self._data.get_selection().__str__()+self._selection.__str__() or 'none')
[2053]1538 headstr.append('***Selections***\n'+ssel)
[1824]1539
[2051]1540 if plot:
[2451]1541 errmsg = "Can plot header only after the first call to plot()."
[2453]1542 self._assert_plotter(action="halt",errmsg=errmsg)
[1819]1543 self._plotter.hold()
[2053]1544 self._header_plot(headstr,fontsize=fontsize)
[1819]1545 import time
[2106]1546 self._plotter.figure.text(0.99,0.01,
[1819]1547 time.strftime("%a %d %b %Y %H:%M:%S %Z"),
1548 horizontalalignment='right',
1549 verticalalignment='bottom',fontsize=8)
1550 self._plotter.release()
1551 if logger:
[2053]1552 selstr = "Selections: "+ssel
[1819]1553 asaplog.push("----------------\n Plot Summary\n----------------")
[2053]1554 asaplog.push(extrastr)
[2290]1555 asaplog.push(ssum[0:ssum.find('Selection:')]\
[2112]1556 + selstr)
[2053]1557 self._headtext['string'] = headstr
1558 del ssel, ssum, headstr
[2051]1559
[2053]1560 def _header_plot(self, texts, fontsize=9):
1561 self._headtext['textobj']=[]
1562 nstcol=len(texts)
1563 for i in range(nstcol):
1564 self._headtext['textobj'].append(
1565 self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1566 texts[i],
1567 horizontalalignment='left',
1568 verticalalignment='top',
1569 fontsize=fontsize))
1570
1571 def clear_header(self):
1572 if not self._headtext['textobj']:
1573 asaplog.push("No header has been plotted. Exit without any operation")
1574 asaplog.post("WARN")
[2453]1575 elif self._assert_plotter(action="status"):
[2053]1576 self._plotter.hold()
1577 for textobj in self._headtext['textobj']:
1578 #if textobj.get_text() in self._headstring:
1579 try:
1580 textobj.remove()
1581 except NotImplementedError:
1582 self._plotter.figure.texts.pop(self._plotter.figure.texts.index(textobj))
1583 self._plotter.release()
1584 self._reset_header()
Note: See TracBrowser for help on using the repository browser.