00001 from asap.parameters import rcParams
00002 from asap.selector import selector
00003 from asap.scantable import scantable
00004 from asap.logging import asaplog, asaplog_post_dec
00005 import matplotlib.axes
00006 from matplotlib.font_manager import FontProperties
00007 from matplotlib.text import Text
00008 from matplotlib import _pylab_helpers
00009
00010 import re
00011
00012 def new_asaplot(visible=None,**kwargs):
00013 """
00014 Returns a new asaplot instance based on the backend settings.
00015 """
00016 if visible == None:
00017 visible = rcParams['plotter.gui']
00018
00019 backend=matplotlib.get_backend()
00020 if not visible:
00021 from asap.asaplot import asaplot
00022 elif backend == 'TkAgg':
00023 from asap.asaplotgui import asaplotgui as asaplot
00024 elif backend == 'Qt4Agg':
00025 from asap.asaplotgui_qt4 import asaplotgui as asaplot
00026 elif backend == 'GTkAgg':
00027 from asap.asaplotgui_gtk import asaplotgui as asaplot
00028 else:
00029 from asap.asaplot import asaplot
00030 return asaplot(**kwargs)
00031
00032 class asapplotter:
00033 """
00034 The ASAP plotter.
00035 By default the plotter is set up to plot polarisations
00036 'colour stacked' and scantables across panels.
00037
00038 .. note::
00039
00040 Currenly it only plots 'spectra' not Tsys or
00041 other variables.
00042
00043 """
00044 def __init__(self, visible=None , **kwargs):
00045 self._visible = rcParams['plotter.gui']
00046 if visible is not None:
00047 self._visible = visible
00048 self._plotter = None
00049 self._inikwg = kwargs
00050
00051 self._panelling = None
00052 self._stacking = None
00053 self.set_panelling()
00054 self.set_stacking()
00055 self._rows = None
00056 self._cols = None
00057 self._minmaxx = None
00058 self._minmaxy = None
00059 self._datamask = None
00060 self._data = None
00061 self._lmap = None
00062 self._title = None
00063 self._ordinate = None
00064 self._abcissa = None
00065 self._abcunit = None
00066 self._usermask = []
00067 self._maskselection = None
00068 self._selection = selector()
00069 self._hist = rcParams['plotter.histogram']
00070 self._fp = FontProperties()
00071 self._margins = self.set_margin(refresh=False)
00072 self._offset = None
00073 self._startrow = 0
00074 self._ipanel = -1
00075 self._panelrows = []
00076 self._headtext={'string': None, 'textobj': None}
00077 self._colormap = None
00078 self._linestyles = None
00079 self._legendloc = None
00080
00081 def _translate(self, instr):
00082 keys = "s b i p t r".split()
00083 if isinstance(instr, str):
00084 for key in keys:
00085 if instr.lower().startswith(key):
00086 return key
00087 return None
00088
00089 @asaplog_post_dec
00090 def _reload_plotter(self):
00091 if self._plotter is not None:
00092
00093
00094
00095
00096
00097
00098 if self.casabar_exists():
00099 del self._plotter.figmgr.casabar
00100 self._plotter.quit()
00101 del self._plotter
00102 asaplog.push('Loading new plotter')
00103 self._plotter = new_asaplot(self._visible,**self._inikwg)
00104 self._plotter.figmgr.casabar=self._new_custombar()
00105
00106 self._plotter.palette(color=0,colormap=self._colormap,
00107 linestyle=0,linestyles=self._linestyles)
00108 self._plotter.legend(self._legendloc)
00109
00110 def _new_custombar(self):
00111 backend=matplotlib.get_backend()
00112 if not self._visible:
00113 return None
00114 elif backend == "TkAgg":
00115 from asap.customgui_tkagg import CustomToolbarTkAgg
00116 return CustomToolbarTkAgg(self)
00117 elif backend == "Qt4Agg":
00118 from asap.customgui_qt4agg import CustomToolbarQT4Agg
00119 return CustomToolbarQT4Agg(self)
00120 return None
00121
00122 def casabar_exists(self):
00123 if not hasattr(self._plotter.figmgr,'casabar'):
00124 return False
00125 elif self._plotter.figmgr.casabar:
00126 return True
00127 return False
00128
00129 def _assert_plotter(self,action="status",errmsg=None):
00130 """
00131 Check plot window status. Returns True if plot window is alive.
00132 Parameters
00133 action: An action to take if the plotter window is not alive.
00134 ['status'|'reload'|'halt']
00135 The action 'status' simply returns False if asaplot
00136 is not alive. When action='reload', plot window is
00137 reloaded and the method returns True. Finally, an
00138 error is raised when action='halt'.
00139 errmsg: An error (warning) message to send to the logger,
00140 when plot window is not alive.
00141 """
00142 isAlive = (self._plotter is not None) and self._plotter._alive()
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 if isAlive:
00158 return True
00159
00160 haltmsg = "Plotter window has not yet been loaded or is closed."
00161 if type(errmsg)==str and len(errmsg) > 0:
00162 haltmsg = errmsg
00163
00164 if action.upper().startswith("R"):
00165
00166 self._reload_plotter()
00167 return True
00168 elif action.upper().startswith("H"):
00169
00170 asaplog.push(haltmsg)
00171 asaplog.post("ERROR")
00172 raise RuntimeError(haltmsg)
00173 else:
00174 if errmsg:
00175 asaplog.push(errmsg)
00176 asaplog.post("WARN")
00177 return False
00178
00179
00180 @asaplog_post_dec
00181 def plot(self, scan=None):
00182 """
00183 Plot a scantable.
00184 Parameters:
00185 scan: a scantable
00186 Note:
00187 If a scantable was specified in a previous call
00188 to plot, no argument has to be given to 'replot'
00189 NO checking is done that the abcissas of the scantable
00190 are consistent e.g. all 'channel' or all 'velocity' etc.
00191 """
00192 if not self._data and not scan:
00193 msg = "Input is not a scantable"
00194 raise TypeError(msg)
00195 self._startrow = 0
00196 self._ipanel = -1
00197 self._reset_header()
00198 self._panelrows = []
00199
00200 self._assert_plotter(action="reload")
00201 if self.casabar_exists():
00202 self._plotter.figmgr.casabar.set_pagecounter(1)
00203
00204 self._plotter.hold()
00205
00206 if scan:
00207 self.set_data(scan, refresh=False)
00208 self._plotter.palette(color=0,colormap=self._colormap,
00209 linestyle=0,linestyles=self._linestyles)
00210 self._plotter.legend(self._legendloc)
00211 self._plot(self._data)
00212 if self._minmaxy is not None:
00213 self._plotter.set_limits(ylim=self._minmaxy)
00214 if self.casabar_exists(): self._plotter.figmgr.casabar.enable_button()
00215 self._plotter.release()
00216 self._plotter.tidy()
00217 self._plotter.show(hardrefresh=False)
00218 return
00219
00220 def gca(self):
00221 errmsg = "No axis to retun. Need to plot first."
00222 if not self._assert_plotter(action="status",errmsg=errmsg):
00223 return None
00224 return self._plotter.figure.gca()
00225
00226 def refresh(self):
00227 """Do a soft refresh"""
00228 errmsg = "No figure to re-plot. Need to plot first."
00229 self._assert_plotter(action="halt",errmsg=errmsg)
00230
00231 self._plotter.figure.show()
00232
00233 def create_mask(self, nwin=1, panel=0, color=None):
00234 """
00235 Interactively define a mask. It retruns a mask that is equivalent to
00236 the one created manually with scantable.create_mask.
00237 Parameters:
00238 nwin: The number of mask windows to create interactively
00239 default is 1.
00240 panel: Which panel to use for mask selection. This is useful
00241 if different IFs are spread over panels (default 0)
00242 """
00243
00244 if not self._assert_plotter(action="status") or (self._data is None):
00245 msg = "Cannot create mask interactively on plot. Can only create mask after plotting."
00246 asaplog.push( msg )
00247 asaplog.post( "ERROR" )
00248 return []
00249 outmask = []
00250 self._plotter.subplot(panel)
00251 xmin, xmax = self._plotter.axes.get_xlim()
00252 marg = 0.05*(xmax-xmin)
00253 self._plotter.axes.set_xlim(xmin-marg, xmax+marg)
00254 self.refresh()
00255
00256 def cleanup(lines=False, texts=False, refresh=False):
00257 if lines:
00258 del self._plotter.axes.lines[-1]
00259 if texts:
00260 del self._plotter.axes.texts[-1]
00261 if refresh:
00262 self.refresh()
00263
00264 for w in xrange(nwin):
00265 wpos = []
00266 self.text(0.05,1.0, "Add start boundary",
00267 coords="relative", fontsize=10)
00268 point = self._plotter.get_point()
00269 cleanup(texts=True)
00270 if point is None:
00271 continue
00272 wpos.append(point[0])
00273 self.axvline(wpos[0], color=color)
00274 self.text(0.05,1.0, "Add end boundary", coords="relative", fontsize=10)
00275 point = self._plotter.get_point()
00276 cleanup(texts=True, lines=True)
00277 if point is None:
00278 self.refresh()
00279 continue
00280 wpos.append(point[0])
00281 self.axvspan(wpos[0], wpos[1], alpha=0.1,
00282 edgecolor=color, facecolor=color)
00283 ymin, ymax = self._plotter.axes.get_ylim()
00284 outmask.append(wpos)
00285
00286 self._plotter.axes.set_xlim(xmin, xmax)
00287 self.refresh()
00288 if len(outmask) > 0:
00289 return self._data.create_mask(*outmask)
00290 return []
00291
00292
00293 def text(self, *args, **kwargs):
00294 self._assert_plotter(action="reload")
00295 if kwargs.has_key("interactive"):
00296 if kwargs.pop("interactive"):
00297 pos = self._plotter.get_point()
00298 args = tuple(pos)+args
00299 self._axes_callback("text", *args, **kwargs)
00300
00301 text.__doc__ = matplotlib.axes.Axes.text.__doc__
00302
00303 def arrow(self, *args, **kwargs):
00304 self._assert_plotter(action="reload")
00305 if kwargs.has_key("interactive"):
00306 if kwargs.pop("interactive"):
00307 pos = self._plotter.get_region()
00308 dpos = (pos[0][0], pos[0][1],
00309 pos[1][0]-pos[0][0],
00310 pos[1][1] - pos[0][1])
00311 args = dpos + args
00312 self._axes_callback("arrow", *args, **kwargs)
00313
00314 arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
00315
00316 def annotate(self, text, xy=None, xytext=None, **kwargs):
00317 self._assert_plotter(action="reload")
00318 if kwargs.has_key("interactive"):
00319 if kwargs.pop("interactive"):
00320 xy = self._plotter.get_point()
00321 xytext = self._plotter.get_point()
00322 if not kwargs.has_key("arrowprops"):
00323 kwargs["arrowprops"] = dict(arrowstyle="->")
00324 self._axes_callback("annotate", text, xy, xytext, **kwargs)
00325
00326 annotate.__doc__ = matplotlib.axes.Axes.annotate.__doc__
00327
00328 def axvline(self, *args, **kwargs):
00329 self._assert_plotter(action="reload")
00330 if kwargs.has_key("interactive"):
00331 if kwargs.pop("interactive"):
00332 pos = self._plotter.get_point()
00333 args = (pos[0],)+args
00334 self._axes_callback("axvline", *args, **kwargs)
00335
00336 axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
00337
00338 def axhline(self, *args, **kwargs):
00339 self._assert_plotter(action="reload")
00340 if kwargs.has_key("interactive"):
00341 if kwargs.pop("interactive"):
00342 pos = self._plotter.get_point()
00343 args = (pos[1],)+args
00344 self._axes_callback("axhline", *args, **kwargs)
00345
00346 axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
00347
00348 def axvspan(self, *args, **kwargs):
00349 self._assert_plotter(action="reload")
00350 if kwargs.has_key("interactive"):
00351 if kwargs.pop("interactive"):
00352 pos = self._plotter.get_region()
00353 dpos = (pos[0][0], pos[1][0])
00354 args = dpos + args
00355 self._axes_callback("axvspan", *args, **kwargs)
00356
00357
00358
00359
00360
00361 axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
00362
00363 def axhspan(self, *args, **kwargs):
00364 self._assert_plotter(action="reload")
00365 if kwargs.has_key("interactive"):
00366 if kwargs.pop("interactive"):
00367 pos = self._plotter.get_region()
00368 dpos = (pos[0][1], pos[1][1])
00369 args = dpos + args
00370 self._axes_callback("axhspan", *args, **kwargs)
00371
00372
00373
00374
00375
00376 axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
00377
00378 def _axes_callback(self, axesfunc, *args, **kwargs):
00379 self._assert_plotter(action="reload")
00380 panel = 0
00381 if kwargs.has_key("panel"):
00382 panel = kwargs.pop("panel")
00383 coords = None
00384 if kwargs.has_key("coords"):
00385 coords = kwargs.pop("coords")
00386 if coords.lower() == 'world':
00387 kwargs["transform"] = self._plotter.axes.transData
00388 elif coords.lower() == 'relative':
00389 kwargs["transform"] = self._plotter.axes.transAxes
00390 self._plotter.subplot(panel)
00391 self._plotter.axes.set_autoscale_on(False)
00392 getattr(self._plotter.axes, axesfunc)(*args, **kwargs)
00393 self._plotter.show(False)
00394 self._plotter.axes.set_autoscale_on(True)
00395
00396
00397 @asaplog_post_dec
00398 def set_data(self, scan, refresh=True):
00399 """
00400 Set a scantable to plot.
00401 Parameters:
00402 scan: a scantable
00403 refresh: True (default) or False. If True, the plot is
00404 replotted based on the new parameter setting(s).
00405 Otherwise,the parameter(s) are set without replotting.
00406 Note:
00407 The user specified masks and data selections will be reset
00408 if a new scantable is set. This method should be called before
00409 setting data selections (set_selection) and/or masks (set_mask).
00410 """
00411 from asap import scantable
00412 if isinstance(scan, scantable):
00413 if (self._data is not None) and (scan != self._data):
00414 del self._data
00415 msg = "A new scantable is set to the plotter. "\
00416 "The masks and data selections are reset."
00417 asaplog.push( msg )
00418 self._data = scan
00419
00420 self._reset()
00421 else:
00422 msg = "Input is not a scantable"
00423 raise TypeError(msg)
00424
00425
00426 if self._abcunit and self._abcunit != self._data.get_unit():
00427 self._minmaxx = None
00428 self._minmaxy = None
00429 self._abcunit = self._data.get_unit()
00430 self._datamask = None
00431 if refresh: self.plot()
00432
00433 @asaplog_post_dec
00434 def set_mode(self, stacking=None, panelling=None, refresh=True):
00435 """
00436 Set the plots look and feel, i.e. what you want to see on the plot.
00437 Parameters:
00438 stacking: tell the plotter which variable to plot
00439 as line colour overlays (default 'pol')
00440 panelling: tell the plotter which variable to plot
00441 across multiple panels (default 'scan'
00442 refresh: True (default) or False. If True, the plot is
00443 replotted based on the new parameter setting(s).
00444 Otherwise,the parameter(s) are set without replotting.
00445 Note:
00446 Valid modes are:
00447 'beam' 'Beam' 'b': Beams
00448 'if' 'IF' 'i': IFs
00449 'pol' 'Pol' 'p': Polarisations
00450 'scan' 'Scan' 's': Scans
00451 'time' 'Time' 't': Times
00452 'row' 'Row' 'r': Rows
00453 When either 'stacking' or 'panelling' is set to 'row',
00454 the other parameter setting is ignored.
00455 """
00456 msg = "Invalid mode"
00457 if not self.set_panelling(panelling) or \
00458 not self.set_stacking(stacking):
00459 raise TypeError(msg)
00460 #if self._panelling == 'r':
00461
00462
00463
00464 if refresh and self._data: self.plot(self._data)
00465 return
00466
00467 def set_panelling(self, what=None):
00468 """Set the 'panelling' mode i.e. which type of spectra should be
00469 spread across different panels.
00470 """
00471
00472 mode = what
00473 if mode is None:
00474 mode = rcParams['plotter.panelling']
00475 md = self._translate(mode)
00476 if md:
00477 self._panelling = md
00478 self._title = None
00479
00480
00481
00482 self._reset_counters()
00483 return True
00484 return False
00485
00486 def set_layout(self,rows=None,cols=None,refresh=True):
00487 """
00488 Set the multi-panel layout, i.e. how many rows and columns plots
00489 are visible.
00490 Parameters:
00491 rows: The number of rows of plots
00492 cols: The number of columns of plots
00493 refresh: True (default) or False. If True, the plot is
00494 replotted based on the new parameter setting(s).
00495 Otherwise,the parameter(s) are set without replotting.
00496 Note:
00497 If no argument is given, the potter reverts to its auto-plot
00498 behaviour.
00499 """
00500 self._rows = rows
00501 self._cols = cols
00502 if refresh and self._data: self.plot(self._data)
00503 return
00504
00505 def set_stacking(self, what=None):
00506 """Set the 'stacking' mode i.e. which type of spectra should be
00507 overlayed.
00508 """
00509 mode = what
00510 if mode is None:
00511 mode = rcParams['plotter.stacking']
00512 md = self._translate(mode)
00513 if md:
00514 self._stacking = md
00515 self._lmap = None
00516
00517
00518
00519 self._reset_counters()
00520 return True
00521 return False
00522
00523 def _reset_counters(self):
00524 self._startrow = 0
00525 self._ipanel = -1
00526 self._panelrows = []
00527
00528 def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True, offset=None):
00529 """
00530 Set the range of interest on the abcissa of the plot
00531 Parameters:
00532 [x,y]start,[x,y]end: The start and end points of the 'zoom' window
00533 refresh: True (default) or False. If True, the plot is
00534 replotted based on the new parameter setting(s).
00535 Otherwise,the parameter(s) are set without replotting.
00536 offset: shift the abcissa by the given amount. The abcissa label will
00537 have '(relative)' appended to it.
00538 Note:
00539 These become non-sensical when the unit changes.
00540 use plotter.set_range() without parameters to reset
00541
00542 """
00543 self._offset = offset
00544 if xstart is None and xend is None:
00545 self._minmaxx = None
00546 else:
00547 self._minmaxx = [xstart,xend]
00548 if ystart is None and yend is None:
00549 self._minmaxy = None
00550 else:
00551 self._minmaxy = [ystart,yend]
00552 if refresh and self._data: self.plot(self._data)
00553 return
00554
00555 def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
00556 """
00557 Specify a mapping for the legend instead of using the default
00558 indices:
00559 Parameters:
00560 mp: a list of 'strings'. This should have the same length
00561 as the number of elements on the legend and then maps
00562 to the indeces in order. It is possible to uses latex
00563 math expression. These have to be enclosed in r'',
00564 e.g. r'$x^{2}$'
00565 fontsize: The font size of the label (default None)
00566 mode: where to display the legend
00567 Any other value for loc else disables the legend:
00568 0: auto
00569 1: upper right
00570 2: upper left
00571 3: lower left
00572 4: lower right
00573 5: right
00574 6: center left
00575 7: center right
00576 8: lower center
00577 9: upper center
00578 10: center
00579 refresh: True (default) or False. If True, the plot is
00580 replotted based on the new parameter setting(s).
00581 Otherwise,the parameter(s) are set without replotting.
00582
00583 Example:
00584 If the data has two IFs/rest frequencies with index 0 and 1
00585 for CO and SiO:
00586 plotter.set_stacking('i')
00587 plotter.set_legend(['CO','SiO'])
00588 plotter.plot()
00589 plotter.set_legend([r'$^{12}CO$', r'SiO'])
00590 """
00591 self._lmap = mp
00592
00593 self._legendloc = mode
00594 if isinstance(fontsize, int):
00595 from matplotlib import rc as rcp
00596 rcp('legend', fontsize=fontsize)
00597 if refresh and self._data: self.plot(self._data)
00598 return
00599
00600 def set_title(self, title=None, fontsize=None, refresh=True):
00601 """
00602 Set the title of sub-plots. If multiple sub-plots are plotted,
00603 multiple titles have to be specified.
00604 Parameters:
00605 title: a list of titles of sub-plots.
00606 fontsize: a font size of titles (integer)
00607 refresh: True (default) or False. If True, the plot is
00608 replotted based on the new parameter setting(s).
00609 Otherwise,the parameter(s) are set without replotting.
00610 Example:
00611 # two panels are visible on the plotter
00612 plotter.set_title(['First Panel','Second Panel'])
00613 """
00614 self._title = title
00615 if isinstance(fontsize, int):
00616 from matplotlib import rc as rcp
00617 rcp('axes', titlesize=fontsize)
00618 if refresh and self._data: self.plot(self._data)
00619 return
00620
00621 def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
00622 """
00623 Set the y-axis label of the plot. If multiple panels are plotted,
00624 multiple labels have to be specified.
00625 Parameters:
00626 ordinate: a list of ordinate labels. None (default) let
00627 data determine the labels
00628 fontsize: a font size of vertical axis labels (integer)
00629 refresh: True (default) or False. If True, the plot is
00630 replotted based on the new parameter setting(s).
00631 Otherwise,the parameter(s) are set without replotting.
00632 Example:
00633 # two panels are visible on the plotter
00634 plotter.set_ordinate(['First Y-Axis','Second Y-Axis'])
00635 """
00636 self._ordinate = ordinate
00637 if isinstance(fontsize, int):
00638 from matplotlib import rc as rcp
00639 rcp('axes', labelsize=fontsize)
00640 rcp('ytick', labelsize=fontsize)
00641 if refresh and self._data: self.plot(self._data)
00642 return
00643
00644 def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
00645 """
00646 Set the x-axis label of the plot. If multiple panels are plotted,
00647 multiple labels have to be specified.
00648 Parameters:
00649 abcissa: a list of abcissa labels. None (default) let
00650 data determine the labels
00651 fontsize: a font size of horizontal axis labels (integer)
00652 refresh: True (default) or False. If True, the plot is
00653 replotted based on the new parameter setting(s).
00654 Otherwise,the parameter(s) are set without replotting.
00655 Example:
00656 # two panels are visible on the plotter
00657 plotter.set_ordinate(['First X-Axis','Second X-Axis'])
00658 """
00659 self._abcissa = abcissa
00660 if isinstance(fontsize, int):
00661 from matplotlib import rc as rcp
00662 rcp('axes', labelsize=fontsize)
00663 rcp('xtick', labelsize=fontsize)
00664 if refresh and self._data: self.plot(self._data)
00665 return
00666
00667 def set_colors(self, colmap, refresh=True):
00668 """
00669 Set the colours to be used. The plotter will cycle through
00670 these colours when lines are overlaid (stacking mode).
00671 Parameters:
00672 colmap: a list of colour names
00673 refresh: True (default) or False. If True, the plot is
00674 replotted based on the new parameter setting(s).
00675 Otherwise,the parameter(s) are set without replotting.
00676 Example:
00677 plotter.set_colors('red green blue')
00678 # If for example four lines are overlaid e.g I Q U V
00679 # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
00680 # and 'V' will be 'red' again.
00681 """
00682
00683
00684
00685 self._colormap = colmap
00686 if refresh and self._data: self.plot(self._data)
00687
00688
00689 set_colours = set_colors
00690
00691 def set_histogram(self, hist=True, linewidth=None, refresh=True):
00692 """
00693 Enable/Disable histogram-like plotting.
00694 Parameters:
00695 hist: True (default) or False. The fisrt default
00696 is taken from the .asaprc setting
00697 plotter.histogram
00698 linewidth: a line width
00699 refresh: True (default) or False. If True, the plot is
00700 replotted based on the new parameter setting(s).
00701 Otherwise,the parameter(s) are set without replotting.
00702 """
00703 self._hist = hist
00704 if isinstance(linewidth, float) or isinstance(linewidth, int):
00705 from matplotlib import rc as rcp
00706 rcp('lines', linewidth=linewidth)
00707 if refresh and self._data: self.plot(self._data)
00708
00709 def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
00710 """
00711 Set the linestyles to be used. The plotter will cycle through
00712 these linestyles when lines are overlaid (stacking mode) AND
00713 only one color has been set.
00714 Parameters:
00715 linestyles: a list of linestyles to use.
00716 'line', 'dashed', 'dotted', 'dashdot',
00717 'dashdotdot' and 'dashdashdot' are
00718 possible
00719 linewidth: a line width
00720 refresh: True (default) or False. If True, the plot is
00721 replotted based on the new parameter setting(s).
00722 Otherwise,the parameter(s) are set without replotting.
00723 Example:
00724 plotter.set_colors('black')
00725 plotter.set_linestyles('line dashed dotted dashdot')
00726 # If for example four lines are overlaid e.g I Q U V
00727 # 'I' will be 'solid', 'Q' will be 'dashed',
00728 # U will be 'dotted' and 'V' will be 'dashdot'.
00729 """
00730
00731
00732
00733 self._linestyles = linestyles
00734 if isinstance(linewidth, float) or isinstance(linewidth, int):
00735 from matplotlib import rc as rcp
00736 rcp('lines', linewidth=linewidth)
00737 if refresh and self._data: self.plot(self._data)
00738
00739 def set_font(self, refresh=True,**kwargs):
00740 """
00741 Set font properties.
00742 Parameters:
00743 family: one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
00744 style: one of 'normal' (or 'roman'), 'italic' or 'oblique'
00745 weight: one of 'normal or 'bold'
00746 size: the 'general' font size, individual elements can be adjusted
00747 seperately
00748 refresh: True (default) or False. If True, the plot is
00749 replotted based on the new parameter setting(s).
00750 Otherwise,the parameter(s) are set without replotting.
00751 """
00752 from matplotlib import rc as rcp
00753 fdict = {}
00754 for k,v in kwargs.iteritems():
00755 if v:
00756 fdict[k] = v
00757 self._fp = FontProperties(**fdict)
00758 if refresh and self._data: self.plot(self._data)
00759
00760 def set_margin(self,margin=[],refresh=True):
00761 """
00762 Set margins between subplots and plot edges.
00763 Parameters:
00764 margin: a list of margins in figure coordinate (0-1),
00765 i.e., fraction of the figure width or height.
00766 The order of elements should be:
00767 [left, bottom, right, top, horizontal space btw panels,
00768 vertical space btw panels].
00769 refresh: True (default) or False. If True, the plot is
00770 replotted based on the new parameter setting(s).
00771 Otherwise,the parameter(s) are set without replotting.
00772 Note
00773 * When margin is not specified, the values are reset to the defaults
00774 of matplotlib.
00775 * If any element is set to be None, the current value is adopted.
00776 """
00777 if margin == []: self._margins=self._reset_margin()
00778 else:
00779 self._margins=[None]*6
00780 self._margins[0:len(margin)]=margin
00781
00782 if refresh and self._data: self.plot(self._data)
00783
00784 def _reset_margin(self):
00785 ks=map(lambda x: 'figure.subplot.'+x,
00786 ['left','bottom','right','top','hspace','wspace'])
00787 return map(matplotlib.rcParams.get,ks)
00788
00789 def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
00790 location=None):
00791 """
00792 Plot a line catalog.
00793 Parameters:
00794 linecat: the linecatalog to plot
00795 doppler: the velocity shift to apply to the frequencies
00796 deltachan: the number of channels to include each side of the
00797 line to determine a local maximum/minimum
00798 rotate: the rotation (in degrees) for the text label (default 90.0)
00799 location: the location of the line annotation from the 'top',
00800 'bottom' or alternate (None - the default)
00801 Notes:
00802 If the spectrum is flagged no line will be drawn in that location.
00803 """
00804 errmsg = "Cannot plot spectral lines. Need to plot scantable first."
00805 self._assert_plotter(action="halt",errmsg=errmsg)
00806 if not self._data:
00807 raise RuntimeError("No scantable has been plotted yet.")
00808 from asap._asap import linecatalog
00809 if not isinstance(linecat, linecatalog):
00810 raise ValueError("'linecat' isn't of type linecatalog.")
00811 if not self._data.get_unit().endswith("Hz"):
00812 raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
00813 from numpy import ma
00814 for j in range(len(self._plotter.subplots)):
00815 self._plotter.subplot(j)
00816 lims = self._plotter.axes.get_xlim()
00817 for row in range(linecat.nrow()):
00818
00819 base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
00820 restf = linecat.get_frequency(row)/base[self._data.get_unit()]
00821 c = 299792.458
00822 freq = restf*(1.0-doppler/c)
00823 if lims[0] < freq < lims[1]:
00824 if location is None:
00825 loc = 'bottom'
00826 if row%2: loc='top'
00827 else: loc = location
00828 maxys = []
00829 for line in self._plotter.axes.lines:
00830 v = line._x
00831 asc = v[0] < v[-1]
00832
00833 idx = None
00834 if not asc:
00835 if v[len(v)-1] <= freq <= v[0]:
00836 i = len(v)-1
00837 while i>=0 and v[i] < freq:
00838 idx = i
00839 i-=1
00840 else:
00841 if v[0] <= freq <= v[len(v)-1]:
00842 i = 0
00843 while i<len(v) and v[i] < freq:
00844 idx = i
00845 i+=1
00846 if idx is not None:
00847 lower = idx - deltachan
00848 upper = idx + deltachan
00849 if lower < 0: lower = 0
00850 if upper > len(v): upper = len(v)
00851 s = slice(lower, upper)
00852 y = line._y[s]
00853 maxy = ma.maximum(y)
00854 if isinstance( maxy, float):
00855 maxys.append(maxy)
00856 if len(maxys):
00857 peak = max(maxys)
00858 if peak > self._plotter.axes.get_ylim()[1]:
00859 loc = 'bottom'
00860 else:
00861 continue
00862 self._plotter.vline_with_label(freq, peak,
00863 linecat.get_name(row),
00864 location=loc, rotate=rotate)
00865 self._plotter.show(hardrefresh=False)
00866
00867
00868 def save(self, filename=None, orientation=None, dpi=None):
00869 """
00870 Save the plot to a file. The known formats are 'png', 'ps', 'eps'.
00871 Parameters:
00872 filename: The name of the output file. This is optional
00873 and autodetects the image format from the file
00874 suffix. If non filename is specified a file
00875 called 'yyyymmdd_hhmmss.png' is created in the
00876 current directory.
00877 orientation: optional parameter for postscript only (not eps).
00878 'landscape', 'portrait' or None (default) are valid.
00879 If None is choosen for 'ps' output, the plot is
00880 automatically oriented to fill the page.
00881 dpi: The dpi of the output non-ps plot
00882 """
00883 errmsg = "Cannot save figure. Need to plot first."
00884 self._assert_plotter(action="halt",errmsg=errmsg)
00885
00886 self._plotter.save(filename,orientation,dpi)
00887 return
00888
00889 @asaplog_post_dec
00890 def set_mask(self, mask=None, selection=None, refresh=True):
00891 """
00892 Set a plotting mask for a specific polarization.
00893 This is useful for masking out 'noise' Pangle outside a source.
00894 Parameters:
00895 mask: a mask from scantable.create_mask
00896 selection: the spectra to apply the mask to.
00897 refresh: True (default) or False. If True, the plot is
00898 replotted based on the new parameter setting(s).
00899 Otherwise,the parameter(s) are set without replotting.
00900 Example:
00901 select = selector()
00902 select.setpolstrings('Pangle')
00903 plotter.set_mask(mymask, select)
00904 """
00905 if not self._data:
00906 msg = "Can only set mask after a first call to plot()"
00907 raise RuntimeError(msg)
00908 if len(mask):
00909 if isinstance(mask, list) or isinstance(mask, tuple):
00910 self._usermask = array(mask)
00911 else:
00912 self._usermask = mask
00913 if mask is None and selection is None:
00914 self._usermask = []
00915 self._maskselection = None
00916 if isinstance(selection, selector):
00917 self._maskselection = {'b': selection.get_beams(),
00918 's': selection.get_scans(),
00919 'i': selection.get_ifs(),
00920 'p': selection.get_pols(),
00921 't': [] }
00922 else:
00923 self._maskselection = None
00924 if refresh: self.plot(self._data)
00925
00926 def _slice_indeces(self, data):
00927 mn = self._minmaxx[0]
00928 mx = self._minmaxx[1]
00929 asc = data[0] < data[-1]
00930 start=0
00931 end = len(data)-1
00932 inc = 1
00933 if not asc:
00934 start = len(data)-1
00935 end = 0
00936 inc = -1
00937
00938
00939
00940 minind=start
00941 for ind in xrange(start,end+inc,inc):
00942 if data[ind] > mn: break
00943 minind=ind
00944
00945
00946
00947
00948 maxind=end
00949 for ind in xrange(end,start-inc,-inc):
00950 if data[ind] < mx: break
00951 maxind=ind
00952 start=minind
00953 end=maxind
00954 if start > end:
00955 return end,start+1
00956 elif start < end:
00957 return start,end+1
00958 else:
00959 return start,end
00960
00961 def _reset(self):
00962 self._usermask = []
00963 self._usermaskspectra = None
00964 self._offset = None
00965 self.set_selection(None, False)
00966 self._reset_header()
00967
00968 def _reset_header(self):
00969 self._headtext={'string': None, 'textobj': None}
00970
00971 def _plot(self, scan):
00972 savesel = scan.get_selection()
00973 sel = savesel + self._selection
00974 order = self._get_sortstring([self._panelling,self._stacking])
00975 if order:
00976 sel.set_order(order)
00977 scan.set_selection(sel)
00978 d = {'b': scan.getbeam, 's': scan.getscan,
00979 'i': scan.getif, 'p': scan.getpol, 't': scan.get_time,
00980 'r': int}#, '_r': int}
00981
00982 polmodes = dict(zip(sel.get_pols(), sel.get_poltypes()))
00983
00984
00985 n0,nstack0 = self._get_selected_n(scan)
00986 if isinstance(n0, int): n = n0
00987 else: n = len(n0)
00988 if isinstance(nstack0, int): nstack = nstack0
00989 else: nstack = len(nstack0)
00990
00991 rowstack = False
00992 titlemode = self._panelling
00993 if self._stacking == "r" and self._panelling != "r":
00994 rowstack = True
00995 titlemode = '_r'
00996 nptot = n
00997 maxpanel, maxstack = 16,16
00998 if nstack > maxstack:
00999 msg ="Scan to be overlayed contains more than %d selections.\n" \
01000 "Selecting first %d selections..." % (maxstack, maxstack)
01001 asaplog.push(msg)
01002 asaplog.post('WARN')
01003 nstack = min(nstack,maxstack)
01004
01005 n = n-self._ipanel-1
01006
01007 ganged = False
01008 if n > 1:
01009 ganged = rcParams['plotter.ganged']
01010 if self._panelling == 'i':
01011 ganged = False
01012 if self._rows and self._cols:
01013 n = min(n,self._rows*self._cols)
01014 self._plotter.set_panels(rows=self._rows,cols=self._cols,
01015 nplots=n,margin=self._margins,
01016 ganged=ganged)
01017 else:
01018 n = min(n,maxpanel)
01019 self._plotter.set_panels(rows=n,cols=0,nplots=n,
01020 margin=self._margins,ganged=ganged)
01021 else:
01022 self._plotter.set_panels(margin=self._margins)
01023
01024 r = self._startrow
01025 nr = scan.nrow()
01026 a0,b0 = -1,-1
01027 allxlim = []
01028 allylim = []
01029
01030 newpanel=False
01031 panelcount,stackcount = 0,0
01032
01033 if r > 0:
01034
01035 a0 = d[self._panelling](r-1)
01036
01037
01038 stackcount = nstack
01039
01040 while r < nr:
01041 a = d[self._panelling](r)
01042 b = d[self._stacking](r)
01043 if a > a0 and panelcount < n:
01044 if n > 1:
01045 self._plotter.subplot(panelcount)
01046 self._plotter.palette(0)
01047
01048 xlab = self._abcissa and self._abcissa[panelcount] \
01049 or scan._getabcissalabel()
01050 if self._offset and not self._abcissa:
01051 xlab += " (relative)"
01052 ylab = self._ordinate and self._ordinate[panelcount] \
01053 or scan._get_ordinate_label()
01054 self._plotter.set_axes('xlabel', xlab)
01055 self._plotter.set_axes('ylabel', ylab)
01056
01057 lbl = self._get_label(scan, r, titlemode, self._title)
01058 if isinstance(lbl, list) or isinstance(lbl, tuple):
01059 if 0 <= panelcount < len(lbl):
01060 lbl = lbl[panelcount]
01061 else:
01062
01063
01064 lbl = self._get_label(scan, r, titlemode, None)
01065 self._plotter.set_axes('title',lbl)
01066 newpanel = True
01067 stackcount = 0
01068 panelcount += 1
01069
01070 if self._panelling != 'r' and \
01071 len(self._panelrows) < self._ipanel+1+panelcount:
01072 self._panelrows += [r]
01073
01074
01075 if stackcount < nstack and (newpanel or \
01076 rowstack or (a == a0 and b > b0)):
01077 y = []
01078 if len(polmodes):
01079 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
01080 else:
01081 y = scan._getspectrum(r)
01082
01083 mr = scan._getflagrow(r)
01084 from numpy import ma, array
01085 if mr:
01086 y = ma.masked_array(y,mask=mr)
01087 else:
01088 m = scan._getmask(r)
01089 from numpy import logical_not, logical_and
01090 if self._maskselection and len(self._usermask) == len(m):
01091 if d[self._stacking](r) in self._maskselection[self._stacking]:
01092 m = logical_and(m, self._usermask)
01093 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
01094
01095 x = array(scan._getabcissa(r))
01096 if self._offset:
01097 x += self._offset
01098 if self._minmaxx is not None:
01099 s,e = self._slice_indeces(x)
01100 x = x[s:e]
01101 y = y[s:e]
01102 if len(x) > 1024 and rcParams['plotter.decimate']:
01103 fac = len(x)/1024
01104 x = x[::fac]
01105 y = y[::fac]
01106 llbl = self._get_label(scan, r, self._stacking, self._lmap)
01107 if isinstance(llbl, list) or isinstance(llbl, tuple):
01108 if 0 <= stackcount < len(llbl):
01109
01110 llbl = llbl[stackcount]
01111 else:
01112
01113 llbl = self._get_label(scan, r, self._stacking, None)
01114 self._plotter.set_line(label=llbl)
01115 plotit = self._plotter.plot
01116 if self._hist: plotit = self._plotter.hist
01117 if len(x) > 0 and not mr:
01118 plotit(x,y)
01119 xlim= self._minmaxx or [min(x),max(x)]
01120 allxlim += xlim
01121 ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
01122 allylim += ylim
01123 else:
01124 xlim = self._minmaxx or []
01125 allxlim += xlim
01126 ylim= self._minmaxy or []
01127 allylim += ylim
01128 stackcount += 1
01129 a0=a
01130 b0=b
01131
01132 if stackcount == nstack and len(allxlim) > 0:
01133 allxlim.sort()
01134 self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
01135 if ganged:
01136 allxlim = [allxlim[0],allxlim[-1]]
01137 else:
01138
01139 allxlim =[]
01140
01141 newpanel = False
01142
01143
01144
01145 if (panelcount == n and stackcount == nstack) or (r == nr-1):
01146
01147
01148 if ganged and len(allylim) > 0:
01149 allylim.sort()
01150 self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
01151 break
01152 r+=1
01153
01154
01155 self._startrow = r+1
01156 self._ipanel += panelcount
01157 if self.casabar_exists():
01158 if self._ipanel >= nptot-1:
01159 self._plotter.figmgr.casabar.disable_next()
01160 else:
01161 self._plotter.figmgr.casabar.enable_next()
01162 if self._ipanel + 1 - panelcount > 0:
01163 self._plotter.figmgr.casabar.enable_prev()
01164 else:
01165 self._plotter.figmgr.casabar.disable_prev()
01166
01167
01168 scan.set_selection(savesel)
01169
01170
01171
01172 if self._fp is not None and getattr(self._plotter.figure,'findobj',False):
01173 for o in self._plotter.figure.findobj(Text):
01174 o.set_fontproperties(self._fp)
01175
01176 def _get_sortstring(self, lorders):
01177 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
01178 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME', 'r':None, '_r':None }
01179 if not (type(lorders) == list) and not (type(lorders) == tuple):
01180 return None
01181 if len(lorders) > 0:
01182 lsorts = []
01183 for order in lorders:
01184 if order == "r":
01185
01186 return None
01187 ssort = d0[order]
01188 if ssort:
01189 lsorts.append(ssort)
01190 return lsorts
01191 return None
01192
01193 def set_selection(self, selection=None, refresh=True, **kw):
01194 """
01195 Parameters:
01196 selection: a selector object (default unset the selection)
01197 refresh: True (default) or False. If True, the plot is
01198 replotted based on the new parameter setting(s).
01199 Otherwise,the parameter(s) are set without replotting.
01200 """
01201 if selection is None:
01202
01203 if len(kw) == 0:
01204 self._selection = selector()
01205 else:
01206
01207 for k in kw:
01208 if k not in selector.fields:
01209 raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
01210 self._selection = selector(**kw)
01211 elif isinstance(selection, selector):
01212 self._selection = selection
01213 else:
01214 raise TypeError("'selection' is not of type selector")
01215
01216 order = self._get_sortstring([self._panelling,self._stacking])
01217 if order:
01218 self._selection.set_order(order)
01219 if refresh and self._data:
01220 self.plot()
01221
01222 def _get_selected_n(self, scan):
01223 d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
01224 'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle,
01225 'r': scan.nrow}#, '_r': False}
01226 d2 = { 'b': self._selection.get_beams(),
01227 's': self._selection.get_scans(),
01228 'i': self._selection.get_ifs(),
01229 'p': self._selection.get_pols(),
01230 't': self._selection.get_cycles(),
01231 'r': False}#, '_r': 1}
01232 n = d2[self._panelling] or d1[self._panelling]()
01233 nstack = d2[self._stacking] or d1[self._stacking]()
01234
01235 if self._panelling == 'r':
01236 nstack = 1
01237 elif self._stacking == 'r':
01238 n = 1
01239 return n,nstack
01240
01241 def _get_label(self, scan, row, mode, userlabel=None):
01242 if isinstance(userlabel, list) and len(userlabel) == 0:
01243 userlabel = " "
01244 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
01245 if len(pms):
01246 poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
01247 else:
01248 poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
01249 d = {'b': "Beam "+str(scan.getbeam(row)),
01250
01251 's': "Scan "+str(scan.getscan(row))+\
01252 " ("+str(scan._getsourcename(row))+")",
01253 'i': "IF"+str(scan.getif(row)),
01254 'p': poleval,
01255 't': str(scan.get_time(row)),
01256 'r': "row "+str(row),
01257
01258 '_r': "" }
01259 return userlabel or d[mode]
01260
01261 def plotazel(self, scan=None, outfile=None):
01262 """
01263 plot azimuth and elevation versus time of a scantable
01264 """
01265 visible = rcParams['plotter.gui']
01266 from matplotlib import pylab as PL
01267 from matplotlib.dates import DateFormatter
01268 from pytz import timezone
01269 from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
01270 from matplotlib.ticker import MultipleLocator
01271 from numpy import array, pi
01272 if not visible or not self._visible:
01273 PL.ioff()
01274 from matplotlib.backends.backend_agg import FigureCanvasAgg
01275 PL.gcf().canvas.switch_backends(FigureCanvasAgg)
01276 self._data = scan
01277 dates = self._data.get_time(asdatetime=True)
01278 t = PL.date2num(dates)
01279 tz = timezone('UTC')
01280 PL.cla()
01281 PL.ioff()
01282 PL.clf()
01283
01284 if not self._margins or len(self._margins) != 6:
01285 self.set_margin(refresh=False)
01286 lef, bot, rig, top, wsp, hsp = self._margins
01287 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
01288 wspace=wsp,hspace=hsp)
01289
01290 tdel = max(t) - min(t)
01291 ax = PL.subplot(2,1,1)
01292 el = array(self._data.get_elevation())*180./pi
01293 PL.ylabel('El [deg.]')
01294 dstr = dates[0].strftime('%Y/%m/%d')
01295 if tdel > 1.0:
01296 dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
01297 dstr = dstr + " - " + dstr2
01298 majloc = DayLocator()
01299 minloc = HourLocator(range(0,23,12))
01300 timefmt = DateFormatter("%b%d")
01301 elif tdel > 24./60.:
01302 timefmt = DateFormatter('%H:%M')
01303 majloc = HourLocator()
01304 minloc = MinuteLocator(30)
01305 else:
01306 timefmt = DateFormatter('%H:%M')
01307 majloc = MinuteLocator(interval=5)
01308 minloc = SecondLocator(30)
01309
01310 PL.title(dstr)
01311 if tdel == 0.0:
01312 th = (t - PL.floor(t))*24.0
01313 PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
01314 else:
01315 PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
01316
01317 ax.xaxis.set_major_formatter(timefmt)
01318 ax.xaxis.set_major_locator(majloc)
01319 ax.xaxis.set_minor_locator(minloc)
01320 ax.yaxis.grid(True)
01321 yloc = MultipleLocator(30)
01322 ax.set_ylim(0,90)
01323 ax.yaxis.set_major_locator(yloc)
01324 if tdel > 1.0:
01325 labels = ax.get_xticklabels()
01326
01327 PL.setp(labels, fontsize=10)
01328
01329
01330 az = array(self._data.get_azimuth())*180./pi
01331 if min(az) < 0:
01332 for irow in range(len(az)):
01333 if az[irow] < 0: az[irow] += 360.0
01334
01335 ax2 = PL.subplot(2,1,2)
01336
01337 PL.ylabel('Az [deg.]')
01338 if tdel == 0.0:
01339 PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
01340 else:
01341 PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
01342 ax2.xaxis.set_major_formatter(timefmt)
01343 ax2.xaxis.set_major_locator(majloc)
01344 ax2.xaxis.set_minor_locator(minloc)
01345
01346 ax2.set_ylim(0,360)
01347 ax2.yaxis.grid(True)
01348
01349
01350 yloc = MultipleLocator(60)
01351 ax2.yaxis.set_major_locator(yloc)
01352 if tdel > 1.0:
01353 labels = ax2.get_xticklabels()
01354 PL.setp(labels, fontsize=10)
01355 PL.xlabel('Time (UT [day])')
01356 else:
01357 PL.xlabel('Time (UT [hour])')
01358
01359 PL.ion()
01360 PL.draw()
01361 if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
01362 if (outfile is not None):
01363 PL.savefig(outfile)
01364
01365 def plotpointing(self, scan=None, outfile=None):
01366 """
01367 plot telescope pointings
01368 """
01369 visible = rcParams['plotter.gui']
01370 from matplotlib import pylab as PL
01371 from numpy import array, pi
01372 if not visible or not self._visible:
01373 PL.ioff()
01374 from matplotlib.backends.backend_agg import FigureCanvasAgg
01375 PL.gcf().canvas.switch_backends(FigureCanvasAgg)
01376 self._data = scan
01377 dir = array(self._data.get_directionval()).transpose()
01378 ra = dir[0]*180./pi
01379 dec = dir[1]*180./pi
01380 PL.cla()
01381
01382 PL.clf()
01383
01384 if not self._margins or len(self._margins) != 6:
01385 self.set_margin(refresh=False)
01386 lef, bot, rig, top, wsp, hsp = self._margins
01387 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
01388 wspace=wsp,hspace=hsp)
01389 ax = PL.gca()
01390
01391
01392 ax.set_aspect('equal')
01393 PL.plot(ra, dec, 'b,')
01394 PL.xlabel('RA [deg.]')
01395 PL.ylabel('Declination [deg.]')
01396 PL.title('Telescope pointings')
01397 [xmin,xmax,ymin,ymax] = PL.axis()
01398 PL.axis([xmax,xmin,ymin,ymax])
01399 PL.ion()
01400 PL.draw()
01401 if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
01402 if (outfile is not None):
01403 PL.savefig(outfile)
01404
01405
01406
01407 @asaplog_post_dec
01408 def plottp(self, scan=None):
01409 self._assert_plotter(action="reload")
01410 self._plotter.hold()
01411 self._plotter.clear()
01412 from asap import scantable
01413 if not self._data and not scan:
01414 msg = "Input is not a scantable"
01415 raise TypeError(msg)
01416 if isinstance(scan, scantable):
01417 if self._data is not None:
01418 if scan != self._data:
01419 self._data = scan
01420
01421 self._reset()
01422 else:
01423 self._data = scan
01424 self._reset()
01425
01426
01427
01428
01429
01430
01431
01432
01433 if not self._margins or len(self._margins) !=6:
01434 self.set_margin(refresh=False)
01435 lef, bot, rig, top, wsp, hsp = self._margins
01436 self._plotter.figure.subplots_adjust(
01437 left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
01438 if self.casabar_exists(): self._plotter.figmgr.casabar.disable_button()
01439 self._plottp(self._data)
01440 if self._minmaxy is not None:
01441 self._plotter.set_limits(ylim=self._minmaxy)
01442 self._plotter.release()
01443 self._plotter.tidy()
01444 self._plotter.show(hardrefresh=False)
01445 return
01446
01447 def _plottp(self,scan):
01448 """
01449 private method for plotting total power data
01450 """
01451 from numpy import ma, array, arange, logical_not
01452 r=0
01453 nr = scan.nrow()
01454 a0,b0 = -1,-1
01455 allxlim = []
01456 allylim = []
01457 y=[]
01458 self._plotter.set_panels()
01459 self._plotter.palette(0)
01460
01461
01462
01463
01464
01465 xlab = self._abcissa or 'row number'
01466 ylab = self._ordinate or scan._get_ordinate_label()
01467 self._plotter.set_axes('xlabel',xlab)
01468 self._plotter.set_axes('ylabel',ylab)
01469 lbl = self._get_label(scan, r, 's', self._title)
01470 if isinstance(lbl, list) or isinstance(lbl, tuple):
01471
01472
01473
01474
01475 lbl = self._get_label(scan, r, self._panelling, None)
01476 self._plotter.set_axes('title',lbl)
01477 y=array(scan._get_column(scan._getspectrum,-1))
01478 m = array(scan._get_column(scan._getmask,-1))
01479 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
01480 x = arange(len(y))
01481
01482 l,m = y.shape
01483 if m > 1:
01484 y=y.mean(axis=1)
01485 plotit = self._plotter.plot
01486 llbl = self._get_label(scan, r, self._stacking, None)
01487 self._plotter.set_line(label=llbl)
01488 if len(x) > 0:
01489 plotit(x,y)
01490
01491
01492
01493 def figtext(self, *args, **kwargs):
01494 """
01495 Add text to figure at location x,y (relative 0-1 coords).
01496 This method forwards *args and **kwargs to a Matplotlib method,
01497 matplotlib.Figure.text.
01498 See the method help for detailed information.
01499 """
01500 self._assert_plotter(action="reload")
01501 self._plotter.text(*args, **kwargs)
01502
01503
01504
01505
01506 @asaplog_post_dec
01507 def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
01508 """
01509 print data (scantable) header on the plot and/or logger.
01510 To plot the header on the plot, this method should be called after
01511 plotting spectra by the method, asapplotter.plot.
01512 Parameters:
01513 plot: whether or not print header info on the plot.
01514 fontsize: header font size (valid only plot=True)
01515 logger: whether or not print header info on the logger.
01516 selstr: additional selection string (not verified)
01517 extrastr: additional string to print at the beginning (not verified)
01518 """
01519 if not plot and not logger:
01520 return
01521 if not self._data:
01522 raise RuntimeError("No scantable has been set yet.")
01523
01524
01525 ssum=self._data._list_header()
01526
01527 headstr=[ssum[0:ssum.find('Obs. Type:')]]
01528 headstr.append(ssum[ssum.find('Obs. Type:'):ssum.find('Flux Unit:')])
01529 if extrastr != '':
01530 headstr[0]=extrastr+'\n'+headstr[0]
01531 self._headtext['extrastr'] = extrastr
01532 if selstr != '':
01533 selstr += '\n'
01534 self._headtext['selstr'] = selstr
01535 ssel=(selstr+self._data.get_selection().__str__()+self._selection.__str__() or 'none')
01536 headstr.append('***Selections***\n'+ssel)
01537
01538 if plot:
01539 errmsg = "Can plot header only after the first call to plot()."
01540 self._assert_plotter(action="halt",errmsg=errmsg)
01541 self._plotter.hold()
01542 self._header_plot(headstr,fontsize=fontsize)
01543 import time
01544 self._plotter.figure.text(0.99,0.01,
01545 time.strftime("%a %d %b %Y %H:%M:%S %Z"),
01546 horizontalalignment='right',
01547 verticalalignment='bottom',fontsize=8)
01548 self._plotter.release()
01549 if logger:
01550 selstr = "Selections: "+ssel
01551 asaplog.push("----------------\n Plot Summary\n----------------")
01552 asaplog.push(extrastr)
01553 asaplog.push(ssum[0:ssum.find('Selection:')]\
01554 + selstr)
01555 self._headtext['string'] = headstr
01556 del ssel, ssum, headstr
01557
01558 def _header_plot(self, texts, fontsize=9):
01559 self._headtext['textobj']=[]
01560 nstcol=len(texts)
01561 for i in range(nstcol):
01562 self._headtext['textobj'].append(
01563 self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
01564 texts[i],
01565 horizontalalignment='left',
01566 verticalalignment='top',
01567 fontsize=fontsize))
01568
01569 def clear_header(self):
01570 if not self._headtext['textobj']:
01571 asaplog.push("No header has been plotted. Exit without any operation")
01572 asaplog.post("WARN")
01573 elif self._assert_plotter(action="status"):
01574 self._plotter.hold()
01575 for textobj in self._headtext['textobj']:
01576
01577 try:
01578 textobj.remove()
01579 except NotImplementedError:
01580 self._plotter.figure.texts.pop(self._plotter.figure.texts.index(textobj))
01581 self._plotter.release()
01582 self._reset_header()
01583
01584
01585 @asaplog_post_dec
01586 def plotgrid(self, scan=None,center=None,spacing=None,rows=None,cols=None):
01587 """
01588 Plot spectra based on direction.
01589
01590 Parameters:
01591 scan: a scantable to plot
01592 center: the grid center direction (a list) of plots in the
01593 unit of DIRECTION column.
01594 (default) the center of map region
01595 spacing: a list of horizontal (R.A.) and vertical (Dec.)
01596 spacing in the unit of DIRECTION column.
01597 (default) Calculated by the extent of map region and
01598 the number of rows and cols to cover
01599 rows: number of panels (grid points) in horizontal direction
01600 cols: number of panels (grid points) in vertical direction
01601
01602 Note:
01603 - Only the first IFNO, POLNO, and BEAM in the scantable will be
01604 plotted.
01605 - This method doesn't re-grid and average spectra in scantable. Use
01606 asapgrid module to re-grid spectra before plotting with this method.
01607 Only the first spectrum is plotted in case there are multiple
01608 spectra which belong to a grid.
01609 """
01610 from asap import scantable
01611 from numpy import array, ma, cos
01612 if not self._data and not scan:
01613 msg = "No scantable is specified to plot"
01614 raise TypeError(msg)
01615 if scan:
01616 self.set_data(scan, refresh=False)
01617 del scan
01618
01619
01620 if rows:
01621 self._rows = int(rows)
01622 else:
01623 msg = "Number of rows to plot are not specified. "
01624 if self._rows:
01625 msg += "Using previous value = %d" % (self._rows)
01626 asaplog.push(msg)
01627 else:
01628 self._rows = 1
01629 msg += "Setting rows = %d" % (self._rows)
01630 asaplog.post()
01631 asaplog.push(msg)
01632 asaplog.post("WARN")
01633 if cols:
01634 self._cols = int(cols)
01635 else:
01636 msg = "Number of cols to plot are not specified. "
01637 if self._cols:
01638 msg += "Using previous value = %d" % (self._cols)
01639 asaplog.push(msg)
01640 else:
01641 self._cols = 1
01642 msg += "Setting cols = %d" % (self._cols)
01643 asaplog.post()
01644 asaplog.push(msg)
01645 asaplog.post("WARN")
01646
01647
01648 if center is None:
01649
01650 asaplog.push("Grid center is not specified. Automatically calculated from pointing center.")
01651
01652 dirarr = array(self._data.get_directionval()).transpose()
01653
01654 center = [0.5*(dirarr[0].max() + dirarr[0].min()),
01655 0.5*(dirarr[1].max() + dirarr[1].min())]
01656 del dirarr
01657 elif (type(center) in (list, tuple)) and len(center) > 1:
01658 center = center[0:2]
01659 else:
01660 msg = "Direction of grid center should be a list of float (R.A., Dec.)"
01661 raise ValueError, msg
01662 asaplog.push("Grid center: (%f, %f) " % (center[0],center[1]))
01663
01664 if spacing is None:
01665
01666 asaplog.push("Grid spacing not specified. Automatically calculated from map coverage")
01667
01668
01669 dirarr = array(self._data.get_directionval()).transpose()
01670 wx = 2. * max(abs(dirarr[0].max()-center[0]),
01671 abs(dirarr[0].min()-center[0]))
01672 wy = 2. * max(abs(dirarr[1].max()-center[1]),
01673 abs(dirarr[1].min()-center[1]))
01674
01675
01676
01677
01678
01679 xgrid = wx/max(self._cols-1.,1.)
01680 ygrid = wy/max(self._rows-1.,1.)
01681
01682
01683
01684 if xgrid == 0:
01685 xgrid = 1.
01686 if ygrid == 0:
01687 ygrid = 1.
01688
01689 spacing = [- xgrid, - ygrid]
01690 del dirarr, xgrid, ygrid
01691
01692
01693 elif (type(spacing) in (list, tuple)) and len(spacing) > 1:
01694 for i in xrange(2):
01695 val = spacing[i]
01696 if not isinstance(val, float):
01697 raise TypeError("spacing should be a list of float")
01698 if val > 0.:
01699 spacing[i] = -val
01700 spacing = spacing[0:2]
01701
01702 spacing[0] /= cos(center[1])
01703 else:
01704 msg = "Invalid spacing."
01705 raise TypeError(msg)
01706 asaplog.push("Spacing: (%f, %f) (projected)" % (spacing[0],spacing[1]))
01707
01708 ntotpl = self._rows * self._cols
01709 minpos = [center[0]-spacing[0]*self._cols/2.,
01710 center[1]-spacing[1]*self._rows/2.]
01711
01712
01713
01714 ifs = self._data.getifnos()
01715 if len(ifs) > 1:
01716 msg = "Found multiple IFs in scantable. Only the first IF (IFNO=%d) will be plotted." % ifs[0]
01717 asaplog.post()
01718 asaplog.push(msg)
01719 asaplog.post("WARN")
01720 pols = self._data.getpolnos()
01721 if len(pols) > 1:
01722 msg = "Found multiple POLs in scantable. Only the first POL (POLNO=%d) will be plotted." % pols[0]
01723 asaplog.post()
01724 asaplog.push(msg)
01725 asaplog.post("WARN")
01726 beams = self._data.getbeamnos()
01727 if len(beams) > 1:
01728 msg = "Found multiple BEAMs in scantable. Only the first BEAM (BEAMNO=%d) will be plotted." % beams[0]
01729 asaplog.post()
01730 asaplog.push(msg)
01731 asaplog.post("WARN")
01732 self._data.set_selection(ifs=[ifs[0]],pols=[pols[0]],beams=[beams[0]])
01733 if self._data.nrow() > ntotpl:
01734 msg = "Scantable is finely sampled than plotting grids. "\
01735 + "Only the first spectrum is plotted in each grid."
01736 asaplog.post()
01737 asaplog.push(msg)
01738 asaplog.post("WARN")
01739
01740 self._assert_plotter(action="reload")
01741 self._plotter.hold()
01742 self._plotter.clear()
01743 self._plotter.legend()
01744
01745
01746 if not self._margins or len(self._margins) !=6:
01747 self.set_margin(refresh=False)
01748 self._plotter.set_panels(rows=self._rows,cols=self._cols,
01749 nplots=ntotpl,margin=self._margins,ganged=True)
01750 if self.casabar_exists():
01751 self._plotter.figmgr.casabar.set_pagecounter(1)
01752 self._plotter.figmgr.casabar.enable_button()
01753
01754 npl = 0
01755 for irow in range(self._data.nrow()):
01756 pos = self._data.get_directionval(irow)
01757 ix = int((pos[0] - minpos[0])/spacing[0])
01758 if ix < 0 or ix >= self._cols:
01759
01760 continue
01761 iy = int((pos[1]- minpos[1])/spacing[1])
01762 if iy < 0 or iy >= self._cols:
01763
01764 continue
01765 ipanel = ix + iy*self._cols
01766 if len(self._plotter.subplots[ipanel]['lines']) > 0:
01767
01768
01769 continue
01770
01771
01772 npl += 1
01773 self._plotter.subplot(ipanel)
01774 self._plotter.palette(0,colormap=self._colormap, \
01775 linestyle=0,linestyles=self._linestyles)
01776 xlab = self._abcissa and self._abcissa[ipanel] \
01777 or self._data._getabcissalabel(irow)
01778 if self._offset and not self._abcissa:
01779 xlab += " (relative)"
01780 ylab = self._ordinate and self._ordinate[ipanel] \
01781 or self._data._get_ordinate_label()
01782 self._plotter.set_axes('xlabel', xlab)
01783 self._plotter.set_axes('ylabel', ylab)
01784
01785
01786 lbl = self._data.get_direction(irow)
01787 self._plotter.set_axes('title',lbl)
01788
01789 y = self._data._getspectrum(irow)
01790
01791 mr = self._data._getflagrow(irow)
01792 if mr:
01793 y = ma.masked_array(y,mask=mr)
01794 else:
01795 m = self._data._getmask(irow)
01796 from numpy import logical_not, logical_and
01797
01798
01799
01800
01801 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
01802
01803 x = array(self._data._getabcissa(irow))
01804 if self._offset:
01805 x += self._offset
01806 if self._minmaxx is not None:
01807 s,e = self._slice_indeces(x)
01808 x = x[s:e]
01809 y = y[s:e]
01810 if len(x) > 1024 and rcParams['plotter.decimate']:
01811 fac = len(x)/1024
01812 x = x[::fac]
01813 y = y[::fac]
01814 self._plotter.set_line(label=lbl)
01815 plotit = self._plotter.plot
01816 if self._hist: plotit = self._plotter.hist
01817 if len(x) > 0 and not mr:
01818 plotit(x,y)
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828
01829 if npl >= ntotpl:
01830 break
01831
01832 if self._minmaxy is not None:
01833 self._plotter.set_limits(ylim=self._minmaxy)
01834 self._plotter.release()
01835 self._plotter.tidy()
01836 self._plotter.show(hardrefresh=False)
01837 return