source: branches/alma/python/asapplotter.py@ 1728

Last change on this file since 1728 was 1724, checked in by Kana Sugimoto, 15 years ago

New Development: Yes

JIRA Issue: Yes (CAS-1801)

Ready to Release: Yes

Interface Changes: Yes

What Interface Changed: two classes, CustomToolbarTkAgg and CustomToolbarCommon,

are created to a new module casatoolbar

Test Programs: run sdplot with plottype='spectra' and 'totalpower'

Put in Release Notes: Yes/No

Module(s): CASA task sdplot, sdbaseline, sdstat, sdfit

Description:

Two new classes, CustomToolbarTkAgg and CustomToolbarCommon, are
created to a new module casatoolbar in order to remove backend dependency
of sdplot(). The other code are modified accordingly.
The additional toolbar, casabar, is now default in ASAP plotter
(asapplotter.plot and asapplotter.plottp).


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.3 KB
RevLine 
[947]1from asap import rcParams, print_log, selector
[1614]2from asap import asaplog
[1153]3import matplotlib.axes
[1317]4import re
[203]5
6class asapplotter:
[226]7 """
8 The ASAP plotter.
9 By default the plotter is set up to plot polarisations
10 'colour stacked' and scantables across panels.
11 Note:
12 Currenly it only plots 'spectra' not Tsys or
13 other variables.
14 """
[734]15 def __init__(self, visible=None):
16 self._visible = rcParams['plotter.gui']
17 if visible is not None:
18 self._visible = visible
[710]19 self._plotter = self._newplotter()
[1724]20 if self._visible and matplotlib.get_backend() == "TkAgg":
21 from asap.casatoolbar import CustomToolbarTkAgg
22 self._plotter.figmgr.casabar = CustomToolbarTkAgg(self)
[710]23
[554]24 self._panelling = None
25 self._stacking = None
26 self.set_panelling()
27 self.set_stacking()
[377]28 self._rows = None
29 self._cols = None
[203]30 self._autoplot = False
[525]31 self._minmaxx = None
32 self._minmaxy = None
[710]33 self._datamask = None
[203]34 self._data = None
[607]35 self._lmap = None
[226]36 self._title = None
[257]37 self._ordinate = None
38 self._abcissa = None
[709]39 self._abcunit = None
[920]40 self._usermask = []
41 self._maskselection = None
42 self._selection = selector()
[1023]43 self._hist = rcParams['plotter.histogram']
[1723]44 self._panellayout = self.set_panellayout(refresh=False)
[1023]45
[920]46 def _translate(self, instr):
47 keys = "s b i p t".split()
48 if isinstance(instr, str):
49 for key in keys:
50 if instr.lower().startswith(key):
51 return key
52 return None
53
[710]54 def _newplotter(self):
[1724]55 backend=matplotlib.get_backend()
56 if not self._visible:
57 from asap.asaplot import asaplot
58 elif backend == 'TkAgg':
[710]59 from asap.asaplotgui import asaplotgui as asaplot
[1724]60 elif backend == 'Qt4Agg':
61 from asap.asaplotgui_qt4 import asaplotgui as asaplot
62 elif backend == 'GTkAgg':
63 from asap.asaplotgui_gtk import asaplotgui as asaplot
[710]64 else:
65 from asap.asaplot import asaplot
66 return asaplot()
67
68
[935]69 def plot(self, scan=None):
[203]70 """
[920]71 Plot a scantable.
[203]72 Parameters:
[920]73 scan: a scantable
[203]74 Note:
[920]75 If a scantable was specified in a previous call
[203]76 to plot, no argument has to be given to 'replot'
[920]77 NO checking is done that the abcissas of the scantable
[203]78 are consistent e.g. all 'channel' or all 'velocity' etc.
79 """
[710]80 if self._plotter.is_dead:
81 self._plotter = self._newplotter()
[600]82 self._plotter.hold()
[203]83 self._plotter.clear()
[935]84 if not self._data and not scan:
[1101]85 msg = "Input is not a scantable"
86 if rcParams['verbose']:
[1612]87 #print msg
[1614]88 asaplog.push( msg )
89 print_log( 'ERROR' )
[1101]90 return
91 raise TypeError(msg)
[1698]92 if scan: self.set_data(scan,refresh=False)
[920]93 self._plot(self._data)
[709]94 if self._minmaxy is not None:
95 self._plotter.set_limits(ylim=self._minmaxy)
[1724]96 if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.enable_button()
[203]97 self._plotter.release()
[1153]98 self._plotter.tidy()
99 self._plotter.show(hardrefresh=False)
[753]100 print_log()
[203]101 return
102
[1153]103
104 # forwards to matplotlib axes
105 def text(self, *args, **kwargs):
106 self._axes_callback("text", *args, **kwargs)
[1358]107 text.__doc__ = matplotlib.axes.Axes.text.__doc__
[1153]108 def arrow(self, *args, **kwargs):
109 self._axes_callback("arrow", *args, **kwargs)
[1358]110 arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
[1153]111 def axvline(self, *args, **kwargs):
112 self._axes_callback("axvline", *args, **kwargs)
[1358]113 axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
[1153]114 def axhline(self, *args, **kwargs):
115 self._axes_callback("axhline", *args, **kwargs)
[1358]116 axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
[1153]117 def axvspan(self, *args, **kwargs):
118 self._axes_callback("axvspan", *args, **kwargs)
119 # hack to preventy mpl from redrawing the patch
120 # it seem to convert the patch into lines on every draw.
121 # This doesn't happen in a test script???
122 del self._plotter.axes.patches[-1]
[1358]123 axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
[1232]124
[1153]125 def axhspan(self, *args, **kwargs):
[1232]126 self._axes_callback("axhspan", *args, **kwargs)
[1153]127 # hack to preventy mpl from redrawing the patch
128 # it seem to convert the patch into lines on every draw.
129 # This doesn't happen in a test script???
130 del self._plotter.axes.patches[-1]
[1358]131 axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
[1153]132
133 def _axes_callback(self, axesfunc, *args, **kwargs):
134 panel = 0
135 if kwargs.has_key("panel"):
136 panel = kwargs.pop("panel")
137 coords = None
138 if kwargs.has_key("coords"):
139 coords = kwargs.pop("coords")
140 if coords.lower() == 'world':
141 kwargs["transform"] = self._plotter.axes.transData
142 elif coords.lower() == 'relative':
143 kwargs["transform"] = self._plotter.axes.transAxes
144 self._plotter.subplot(panel)
145 self._plotter.axes.set_autoscale_on(False)
146 getattr(self._plotter.axes, axesfunc)(*args, **kwargs)
147 self._plotter.show(False)
148 self._plotter.axes.set_autoscale_on(True)
149 # end matplotlib.axes fowarding functions
150
[1698]151 def set_data(self, scan, refresh=True):
[203]152 """
[1698]153 Set a scantable to plot.
154 Parameters:
155 scan: a scantable
156 refresh: True (default) or False. If True, the plot is
157 replotted based on the new parameter setting(s).
158 Otherwise,the parameter(s) are set without replotting.
[1702]159 Note:
160 The user specified masks and data selections will be reset
161 if a new scantable is set. This method should be called before
162 setting data selections (set_selection) and/or masks (set_mask).
[1698]163 """
164 from asap import scantable
165 if isinstance(scan, scantable):
166 if self._data is not None:
167 if scan != self._data:
168 self._data = scan
169 # reset
170 self._reset()
[1702]171 msg = "A new scantable is set to the plotter. The masks and data selections are reset."
172 asaplog.push( msg )
173 print_log( 'INFO' )
[1698]174 else:
175 self._data = scan
176 self._reset()
177 else:
178 msg = "Input is not a scantable"
179 if rcParams['verbose']:
180 #print msg
181 asaplog.push( msg )
182 print_log( 'ERROR' )
183 return
184 raise TypeError(msg)
185
186 # ranges become invalid when unit changes
187 if self._abcunit and self._abcunit != self._data.get_unit():
188 self._minmaxx = None
189 self._minmaxy = None
190 self._abcunit = self._data.get_unit()
191 self._datamask = None
192 if refresh: self.plot()
193
194
195 def set_mode(self, stacking=None, panelling=None, refresh=True):
196 """
[377]197 Set the plots look and feel, i.e. what you want to see on the plot.
[203]198 Parameters:
199 stacking: tell the plotter which variable to plot
[1217]200 as line colour overlays (default 'pol')
[203]201 panelling: tell the plotter which variable to plot
202 across multiple panels (default 'scan'
[1698]203 refresh: True (default) or False. If True, the plot is
204 replotted based on the new parameter setting(s).
205 Otherwise,the parameter(s) are set without replotting.
[203]206 Note:
207 Valid modes are:
208 'beam' 'Beam' 'b': Beams
209 'if' 'IF' 'i': IFs
210 'pol' 'Pol' 'p': Polarisations
211 'scan' 'Scan' 's': Scans
212 'time' 'Time' 't': Times
213 """
[753]214 msg = "Invalid mode"
215 if not self.set_panelling(panelling) or \
216 not self.set_stacking(stacking):
217 if rcParams['verbose']:
[1612]218 #print msg
[1614]219 asaplog.push( msg )
220 print_log( 'ERROR' )
[753]221 return
222 else:
223 raise TypeError(msg)
[1698]224 if refresh and self._data: self.plot(self._data)
[203]225 return
226
[554]227 def set_panelling(self, what=None):
228 mode = what
229 if mode is None:
230 mode = rcParams['plotter.panelling']
231 md = self._translate(mode)
[203]232 if md:
[554]233 self._panelling = md
[226]234 self._title = None
[203]235 return True
236 return False
237
[1698]238 def set_layout(self,rows=None,cols=None,refresh=True):
[377]239 """
240 Set the multi-panel layout, i.e. how many rows and columns plots
241 are visible.
242 Parameters:
243 rows: The number of rows of plots
244 cols: The number of columns of plots
[1698]245 refresh: True (default) or False. If True, the plot is
246 replotted based on the new parameter setting(s).
247 Otherwise,the parameter(s) are set without replotting.
[377]248 Note:
249 If no argument is given, the potter reverts to its auto-plot
250 behaviour.
251 """
252 self._rows = rows
253 self._cols = cols
[1698]254 if refresh and self._data: self.plot(self._data)
[377]255 return
256
[709]257 def set_stacking(self, what=None):
[554]258 mode = what
[709]259 if mode is None:
260 mode = rcParams['plotter.stacking']
[554]261 md = self._translate(mode)
[203]262 if md:
263 self._stacking = md
[226]264 self._lmap = None
[203]265 return True
266 return False
267
[1698]268 def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True):
[203]269 """
270 Set the range of interest on the abcissa of the plot
271 Parameters:
[525]272 [x,y]start,[x,y]end: The start and end points of the 'zoom' window
[1698]273 refresh: True (default) or False. If True, the plot is
274 replotted based on the new parameter setting(s).
275 Otherwise,the parameter(s) are set without replotting.
[203]276 Note:
277 These become non-sensical when the unit changes.
278 use plotter.set_range() without parameters to reset
279
280 """
[525]281 if xstart is None and xend is None:
282 self._minmaxx = None
[600]283 else:
284 self._minmaxx = [xstart,xend]
[525]285 if ystart is None and yend is None:
286 self._minmaxy = None
[600]287 else:
[709]288 self._minmaxy = [ystart,yend]
[1698]289 if refresh and self._data: self.plot(self._data)
[203]290 return
[709]291
[1698]292 def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
[203]293 """
294 Specify a mapping for the legend instead of using the default
295 indices:
296 Parameters:
[1101]297 mp: a list of 'strings'. This should have the same length
298 as the number of elements on the legend and then maps
299 to the indeces in order. It is possible to uses latex
300 math expression. These have to be enclosed in r'',
301 e.g. r'$x^{2}$'
302 fontsize: The font size of the label (default None)
303 mode: where to display the legend
304 Any other value for loc else disables the legend:
[1096]305 0: auto
306 1: upper right
307 2: upper left
308 3: lower left
309 4: lower right
310 5: right
311 6: center left
312 7: center right
313 8: lower center
314 9: upper center
315 10: center
[1698]316 refresh: True (default) or False. If True, the plot is
317 replotted based on the new parameter setting(s).
318 Otherwise,the parameter(s) are set without replotting.
[203]319
320 Example:
[485]321 If the data has two IFs/rest frequencies with index 0 and 1
[203]322 for CO and SiO:
323 plotter.set_stacking('i')
[710]324 plotter.set_legend(['CO','SiO'])
[203]325 plotter.plot()
[710]326 plotter.set_legend([r'$^{12}CO$', r'SiO'])
[203]327 """
328 self._lmap = mp
[1096]329 self._plotter.legend(mode)
[1101]330 if isinstance(fontsize, int):
331 from matplotlib import rc as rcp
332 rcp('legend', fontsize=fontsize)
[1698]333 if refresh and self._data: self.plot(self._data)
[226]334 return
335
[1698]336 def set_title(self, title=None, fontsize=None, refresh=True):
[710]337 """
338 Set the title of the plot. If multiple panels are plotted,
339 multiple titles have to be specified.
[1698]340 Parameters:
341 refresh: True (default) or False. If True, the plot is
342 replotted based on the new parameter setting(s).
343 Otherwise,the parameter(s) are set without replotting.
[710]344 Example:
345 # two panels are visible on the plotter
346 plotter.set_title(["First Panel","Second Panel"])
347 """
[226]348 self._title = title
[1101]349 if isinstance(fontsize, int):
350 from matplotlib import rc as rcp
351 rcp('axes', titlesize=fontsize)
[1698]352 if refresh and self._data: self.plot(self._data)
[226]353 return
354
[1698]355 def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
[710]356 """
357 Set the y-axis label of the plot. If multiple panels are plotted,
358 multiple labels have to be specified.
[1021]359 Parameters:
360 ordinate: a list of ordinate labels. None (default) let
361 data determine the labels
[1698]362 refresh: True (default) or False. If True, the plot is
363 replotted based on the new parameter setting(s).
364 Otherwise,the parameter(s) are set without replotting.
[710]365 Example:
366 # two panels are visible on the plotter
367 plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
368 """
[257]369 self._ordinate = ordinate
[1101]370 if isinstance(fontsize, int):
371 from matplotlib import rc as rcp
372 rcp('axes', labelsize=fontsize)
373 rcp('ytick', labelsize=fontsize)
[1698]374 if refresh and self._data: self.plot(self._data)
[257]375 return
376
[1698]377 def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
[710]378 """
379 Set the x-axis label of the plot. If multiple panels are plotted,
380 multiple labels have to be specified.
[1021]381 Parameters:
382 abcissa: a list of abcissa labels. None (default) let
383 data determine the labels
[1698]384 refresh: True (default) or False. If True, the plot is
385 replotted based on the new parameter setting(s).
386 Otherwise,the parameter(s) are set without replotting.
[710]387 Example:
388 # two panels are visible on the plotter
389 plotter.set_ordinate(["First X-Axis","Second X-Axis"])
390 """
[257]391 self._abcissa = abcissa
[1101]392 if isinstance(fontsize, int):
393 from matplotlib import rc as rcp
394 rcp('axes', labelsize=fontsize)
395 rcp('xtick', labelsize=fontsize)
[1698]396 if refresh and self._data: self.plot(self._data)
[257]397 return
398
[1698]399 def set_colors(self, colmap, refresh=True):
[377]400 """
[1217]401 Set the colours to be used. The plotter will cycle through
402 these colours when lines are overlaid (stacking mode).
[1021]403 Parameters:
[1217]404 colmap: a list of colour names
[1698]405 refresh: True (default) or False. If True, the plot is
406 replotted based on the new parameter setting(s).
407 Otherwise,the parameter(s) are set without replotting.
[710]408 Example:
409 plotter.set_colors("red green blue")
410 # If for example four lines are overlaid e.g I Q U V
411 # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
412 # and 'V' will be 'red' again.
413 """
[1217]414 if isinstance(colmap,str):
415 colmap = colmap.split()
416 self._plotter.palette(0, colormap=colmap)
[1698]417 if refresh and self._data: self.plot(self._data)
[710]418
[1217]419 # alias for english speakers
420 set_colours = set_colors
421
[1698]422 def set_histogram(self, hist=True, linewidth=None, refresh=True):
[1021]423 """
424 Enable/Disable histogram-like plotting.
425 Parameters:
426 hist: True (default) or False. The fisrt default
427 is taken from the .asaprc setting
428 plotter.histogram
[1698]429 refresh: True (default) or False. If True, the plot is
430 replotted based on the new parameter setting(s).
431 Otherwise,the parameter(s) are set without replotting.
[1021]432 """
[1023]433 self._hist = hist
[1101]434 if isinstance(linewidth, float) or isinstance(linewidth, int):
435 from matplotlib import rc as rcp
436 rcp('lines', linewidth=linewidth)
[1698]437 if refresh and self._data: self.plot(self._data)
[1023]438
[1698]439 def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
[710]440 """
[734]441 Set the linestyles to be used. The plotter will cycle through
442 these linestyles when lines are overlaid (stacking mode) AND
443 only one color has been set.
[710]444 Parameters:
445 linestyles: a list of linestyles to use.
446 'line', 'dashed', 'dotted', 'dashdot',
447 'dashdotdot' and 'dashdashdot' are
448 possible
[1698]449 refresh: True (default) or False. If True, the plot is
450 replotted based on the new parameter setting(s).
451 Otherwise,the parameter(s) are set without replotting.
[710]452 Example:
453 plotter.set_colors("black")
454 plotter.set_linestyles("line dashed dotted dashdot")
455 # If for example four lines are overlaid e.g I Q U V
456 # 'I' will be 'solid', 'Q' will be 'dashed',
457 # U will be 'dotted' and 'V' will be 'dashdot'.
458 """
459 if isinstance(linestyles,str):
460 linestyles = linestyles.split()
461 self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
[1101]462 if isinstance(linewidth, float) or isinstance(linewidth, int):
463 from matplotlib import rc as rcp
464 rcp('lines', linewidth=linewidth)
[1698]465 if refresh and self._data: self.plot(self._data)
[710]466
[1698]467 def set_font(self, family=None, style=None, weight=None, size=None, refresh=True):
[1101]468 """
469 Set font properties.
470 Parameters:
471 family: one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
472 style: one of 'normal' (or 'roman'), 'italic' or 'oblique'
473 weight: one of 'normal or 'bold'
474 size: the 'general' font size, individual elements can be adjusted
475 seperately
[1698]476 refresh: True (default) or False. If True, the plot is
477 replotted based on the new parameter setting(s).
478 Otherwise,the parameter(s) are set without replotting.
[1101]479 """
480 from matplotlib import rc as rcp
481 if isinstance(family, str):
482 rcp('font', family=family)
483 if isinstance(style, str):
484 rcp('font', style=style)
485 if isinstance(weight, str):
486 rcp('font', weight=weight)
487 if isinstance(size, float) or isinstance(size, int):
488 rcp('font', size=size)
[1698]489 if refresh and self._data: self.plot(self._data)
[1101]490
[1723]491 def set_panellayout(self,layout=[],refresh=True):
492 """
493 Set the layout of subplots.
494 Parameters:
495 layout: a list of subplots layout in figure coordinate (0-1),
496 i.e., fraction of the figure width or height.
497 The order of elements should be:
498 [left, bottom, right, top, horizontal space btw panels,
499 vertical space btw panels].
500 refresh: True (default) or False. If True, the plot is
501 replotted based on the new parameter setting(s).
502 Otherwise,the parameter(s) are set without replotting.
503 Note
504 * When layout is not specified, the values are reset to the defaults
505 of matplotlib.
506 * If any element is set to be None, the current value is adopted.
507 """
508 if layout == []: self._panellayout=self._reset_panellayout()
509 else:
510 self._panellayout=[None]*6
511 self._panellayout[0:len(layout)]=layout
512 #print "panel layout set to ",self._panellayout
513 if refresh and self._data: self.plot(self._data)
514
515 def _reset_panellayout(self):
516 ks=map(lambda x: 'figure.subplot.'+x,
517 ['left','bottom','right','top','hspace','wspace'])
518 return map(matplotlib.rcParams.get,ks)
519
[1259]520 def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
[1146]521 location=None):
522 """
[1158]523 Plot a line catalog.
524 Parameters:
525 linecat: the linecatalog to plot
[1168]526 doppler: the velocity shift to apply to the frequencies
[1158]527 deltachan: the number of channels to include each side of the
528 line to determine a local maximum/minimum
[1259]529 rotate: the rotation (in degrees) )for the text label (default 90.0)
[1158]530 location: the location of the line annotation from the 'top',
531 'bottom' or alternate (None - the default)
[1165]532 Notes:
533 If the spectrum is flagged no line will be drawn in that location.
[1146]534 """
[1259]535 if not self._data:
536 raise RuntimeError("No scantable has been plotted yet.")
[1146]537 from asap._asap import linecatalog
[1259]538 if not isinstance(linecat, linecatalog):
539 raise ValueError("'linecat' isn't of type linecatalog.")
540 if not self._data.get_unit().endswith("Hz"):
541 raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
[1153]542 from matplotlib.numerix import ma
[1146]543 for j in range(len(self._plotter.subplots)):
544 self._plotter.subplot(j)
545 lims = self._plotter.axes.get_xlim()
[1153]546 for row in range(linecat.nrow()):
[1259]547 # get_frequency returns MHz
548 base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
549 restf = linecat.get_frequency(row)/base[self._data.get_unit()]
[1165]550 c = 299792.458
[1174]551 freq = restf*(1.0-doppler/c)
[1146]552 if lims[0] < freq < lims[1]:
553 if location is None:
554 loc = 'bottom'
[1153]555 if row%2: loc='top'
[1146]556 else: loc = location
[1153]557 maxys = []
558 for line in self._plotter.axes.lines:
559 v = line._x
560 asc = v[0] < v[-1]
561
562 idx = None
563 if not asc:
564 if v[len(v)-1] <= freq <= v[0]:
565 i = len(v)-1
566 while i>=0 and v[i] < freq:
567 idx = i
568 i-=1
569 else:
570 if v[0] <= freq <= v[len(v)-1]:
571 i = 0
572 while i<len(v) and v[i] < freq:
573 idx = i
574 i+=1
575 if idx is not None:
576 lower = idx - deltachan
577 upper = idx + deltachan
578 if lower < 0: lower = 0
579 if upper > len(v): upper = len(v)
580 s = slice(lower, upper)
[1167]581 y = line._y[s]
[1165]582 maxy = ma.maximum(y)
583 if isinstance( maxy, float):
584 maxys.append(maxy)
[1164]585 if len(maxys):
586 peak = max(maxys)
[1165]587 if peak > self._plotter.axes.get_ylim()[1]:
588 loc = 'bottom'
[1164]589 else:
590 continue
[1157]591 self._plotter.vline_with_label(freq, peak,
592 linecat.get_name(row),
593 location=loc, rotate=rotate)
[1153]594 self._plotter.show(hardrefresh=False)
[1146]595
[1153]596
[710]597 def save(self, filename=None, orientation=None, dpi=None):
598 """
[377]599 Save the plot to a file. The know formats are 'png', 'ps', 'eps'.
600 Parameters:
601 filename: The name of the output file. This is optional
602 and autodetects the image format from the file
603 suffix. If non filename is specified a file
604 called 'yyyymmdd_hhmmss.png' is created in the
605 current directory.
[709]606 orientation: optional parameter for postscript only (not eps).
607 'landscape', 'portrait' or None (default) are valid.
608 If None is choosen for 'ps' output, the plot is
609 automatically oriented to fill the page.
[710]610 dpi: The dpi of the output non-ps plot
[377]611 """
[709]612 self._plotter.save(filename,orientation,dpi)
[377]613 return
[709]614
[257]615
[1698]616 def set_mask(self, mask=None, selection=None, refresh=True):
[525]617 """
[734]618 Set a plotting mask for a specific polarization.
619 This is useful for masking out "noise" Pangle outside a source.
620 Parameters:
[920]621 mask: a mask from scantable.create_mask
622 selection: the spectra to apply the mask to.
[1698]623 refresh: True (default) or False. If True, the plot is
624 replotted based on the new parameter setting(s).
625 Otherwise,the parameter(s) are set without replotting.
[734]626 Example:
[920]627 select = selector()
628 select.setpolstrings("Pangle")
629 plotter.set_mask(mymask, select)
[734]630 """
[710]631 if not self._data:
[920]632 msg = "Can only set mask after a first call to plot()"
[753]633 if rcParams['verbose']:
[1612]634 #print msg
[1614]635 asaplog.push( msg )
636 print_log( 'ERROR' )
[762]637 return
[753]638 else:
[762]639 raise RuntimeError(msg)
[920]640 if len(mask):
641 if isinstance(mask, list) or isinstance(mask, tuple):
642 self._usermask = array(mask)
[710]643 else:
[920]644 self._usermask = mask
645 if mask is None and selection is None:
646 self._usermask = []
647 self._maskselection = None
648 if isinstance(selection, selector):
[947]649 self._maskselection = {'b': selection.get_beams(),
650 's': selection.get_scans(),
651 'i': selection.get_ifs(),
652 'p': selection.get_pols(),
[920]653 't': [] }
[710]654 else:
[920]655 self._maskselection = None
[1698]656 if refresh: self.plot(self._data)
[710]657
[709]658 def _slice_indeces(self, data):
659 mn = self._minmaxx[0]
660 mx = self._minmaxx[1]
661 asc = data[0] < data[-1]
662 start=0
663 end = len(data)-1
664 inc = 1
665 if not asc:
666 start = len(data)-1
667 end = 0
668 inc = -1
669 # find min index
[1562]670 #while start > 0 and data[start] < mn:
671 # start+= inc
672 minind=start
673 for ind in xrange(start,end+inc,inc):
674 if data[ind] > mn: break
675 minind=ind
[709]676 # find max index
[1562]677 #while end > 0 and data[end] > mx:
678 # end-=inc
679 #if end > 0: end +=1
680 maxind=end
681 for ind in xrange(end,start-inc,-inc):
682 if data[ind] < mx: break
683 maxind=ind
684 start=minind
685 end=maxind
[709]686 if start > end:
[1562]687 return end,start+1
688 elif start < end:
689 return start,end+1
690 else:
691 return start,end
[709]692
[710]693 def _reset(self):
[920]694 self._usermask = []
[710]695 self._usermaskspectra = None
[920]696 self.set_selection(None, False)
697
698 def _plot(self, scan):
[947]699 savesel = scan.get_selection()
700 sel = savesel + self._selection
701 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
702 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
703 order = [d0[self._panelling],d0[self._stacking]]
704 sel.set_order(order)
705 scan.set_selection(sel)
[920]706 d = {'b': scan.getbeam, 's': scan.getscan,
707 'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
708
[1148]709 polmodes = dict(zip(self._selection.get_pols(),
710 self._selection.get_poltypes()))
711 # this returns either a tuple of numbers or a length (ncycles)
712 # convert this into lengths
713 n0,nstack0 = self._get_selected_n(scan)
714 if isinstance(n0, int): n = n0
[1175]715 else: n = len(n0)
[1148]716 if isinstance(nstack0, int): nstack = nstack0
[1175]717 else: nstack = len(nstack0)
[998]718 maxpanel, maxstack = 16,8
[920]719 if n > maxpanel or nstack > maxstack:
[1148]720 maxn = 0
721 if nstack > maxstack: maxn = maxstack
722 if n > maxpanel: maxn = maxpanel
[920]723 msg ="Scan to be plotted contains more than %d selections.\n" \
[1148]724 "Selecting first %d selections..." % (maxn, maxn)
[920]725 asaplog.push(msg)
[1614]726 print_log('WARN')
[920]727 n = min(n,maxpanel)
[998]728 nstack = min(nstack,maxstack)
[920]729 if n > 1:
730 ganged = rcParams['plotter.ganged']
[1446]731 if self._panelling == 'i':
732 ganged = False
[920]733 if self._rows and self._cols:
734 n = min(n,self._rows*self._cols)
735 self._plotter.set_panels(rows=self._rows,cols=self._cols,
[1723]736# nplots=n,ganged=ganged)
737 nplots=n,layout=self._panellayout,ganged=ganged)
[920]738 else:
[1723]739# self._plotter.set_panels(rows=n,cols=0,nplots=n,ganged=ganged)
740 self._plotter.set_panels(rows=n,cols=0,nplots=n,layout=self._panellayout,ganged=ganged)
[920]741 else:
[1723]742# self._plotter.set_panels()
743 self._plotter.set_panels(layout=self._panellayout)
[920]744 r=0
745 nr = scan.nrow()
746 a0,b0 = -1,-1
747 allxlim = []
[1018]748 allylim = []
[920]749 newpanel=True
750 panelcount,stackcount = 0,0
[1002]751 while r < nr:
[920]752 a = d[self._panelling](r)
753 b = d[self._stacking](r)
754 if a > a0 and panelcount < n:
755 if n > 1:
756 self._plotter.subplot(panelcount)
757 self._plotter.palette(0)
758 #title
759 xlab = self._abcissa and self._abcissa[panelcount] \
760 or scan._getabcissalabel()
761 ylab = self._ordinate and self._ordinate[panelcount] \
762 or scan._get_ordinate_label()
763 self._plotter.set_axes('xlabel',xlab)
764 self._plotter.set_axes('ylabel',ylab)
765 lbl = self._get_label(scan, r, self._panelling, self._title)
766 if isinstance(lbl, list) or isinstance(lbl, tuple):
767 if 0 <= panelcount < len(lbl):
768 lbl = lbl[panelcount]
769 else:
770 # get default label
771 lbl = self._get_label(scan, r, self._panelling, None)
772 self._plotter.set_axes('title',lbl)
773 newpanel = True
774 stackcount =0
775 panelcount += 1
776 if (b > b0 or newpanel) and stackcount < nstack:
777 y = []
778 if len(polmodes):
779 y = scan._getspectrum(r, polmodes[scan.getpol(r)])
780 else:
781 y = scan._getspectrum(r)
782 m = scan._getmask(r)
[1146]783 from matplotlib.numerix import logical_not, logical_and
[920]784 if self._maskselection and len(self._usermask) == len(m):
785 if d[self._stacking](r) in self._maskselection[self._stacking]:
786 m = logical_and(m, self._usermask)
787 x = scan._getabcissa(r)
[1146]788 from matplotlib.numerix import ma, array
[1116]789 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
[920]790 if self._minmaxx is not None:
791 s,e = self._slice_indeces(x)
792 x = x[s:e]
793 y = y[s:e]
[1096]794 if len(x) > 1024 and rcParams['plotter.decimate']:
795 fac = len(x)/1024
[920]796 x = x[::fac]
797 y = y[::fac]
798 llbl = self._get_label(scan, r, self._stacking, self._lmap)
799 if isinstance(llbl, list) or isinstance(llbl, tuple):
800 if 0 <= stackcount < len(llbl):
801 # use user label
802 llbl = llbl[stackcount]
803 else:
804 # get default label
805 llbl = self._get_label(scan, r, self._stacking, None)
806 self._plotter.set_line(label=llbl)
[1023]807 plotit = self._plotter.plot
808 if self._hist: plotit = self._plotter.hist
[1146]809 if len(x) > 0:
810 plotit(x,y)
811 xlim= self._minmaxx or [min(x),max(x)]
812 allxlim += xlim
813 ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
814 allylim += ylim
[1562]815 else:
816 xlim = self._minmaxx or []
817 allxlim += xlim
818 ylim= self._minmaxy or []
819 allylim += ylim
[920]820 stackcount += 1
821 # last in colour stack -> autoscale x
[1562]822 if stackcount == nstack and len(allxlim) > 0:
[920]823 allxlim.sort()
[1446]824 self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
[920]825 # clear
826 allxlim =[]
827
828 newpanel = False
829 a0=a
830 b0=b
831 # ignore following rows
832 if (panelcount == n) and (stackcount == nstack):
[1018]833 # last panel -> autoscale y if ganged
[1562]834 if rcParams['plotter.ganged'] and len(allylim) > 0:
[1018]835 allylim.sort()
836 self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
[998]837 break
[920]838 r+=1 # next row
[947]839 #reset the selector to the scantable's original
840 scan.set_selection(savesel)
[920]841
842 def set_selection(self, selection=None, refresh=True):
[1698]843 """
844 Parameters:
845 selection: a selector object (default unset the selection)
846 refresh: True (default) or False. If True, the plot is
847 replotted based on the new parameter setting(s).
848 Otherwise,the parameter(s) are set without replotting.
849 """
[947]850 self._selection = isinstance(selection,selector) and selection or selector()
[920]851 d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
852 'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
853 order = [d0[self._panelling],d0[self._stacking]]
[947]854 self._selection.set_order(order)
[1698]855 if refresh and self._data: self.plot(self._data)
[920]856
857 def _get_selected_n(self, scan):
[1148]858 d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
859 'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle }
860 d2 = { 'b': self._selection.get_beams(),
861 's': self._selection.get_scans(),
862 'i': self._selection.get_ifs(),
863 'p': self._selection.get_pols(),
864 't': self._selection.get_cycles() }
[920]865 n = d2[self._panelling] or d1[self._panelling]()
866 nstack = d2[self._stacking] or d1[self._stacking]()
867 return n,nstack
868
869 def _get_label(self, scan, row, mode, userlabel=None):
[1153]870 if isinstance(userlabel, list) and len(userlabel) == 0:
871 userlabel = " "
[947]872 pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
[920]873 if len(pms):
874 poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
875 else:
876 poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
877 d = {'b': "Beam "+str(scan.getbeam(row)),
[1714]878 #'s': scan._getsourcename(row),
879 's': "Scan "+str(scan.getscan(row))+\
880 " ("+str(scan._getsourcename(row))+")",
[920]881 'i': "IF"+str(scan.getif(row)),
[964]882 'p': poleval,
[1175]883 't': str(scan.get_time(row)) }
[920]884 return userlabel or d[mode]
[1153]885
[1446]886 def plotazel(self, scan=None, outfile=None):
[1389]887 """
888 plot azimuth and elevation versus time of a scantable
889 """
890 import pylab as PL
891 from matplotlib.dates import DateFormatter, timezone, HourLocator, MinuteLocator, DayLocator
892 from matplotlib.ticker import MultipleLocator
893 from matplotlib.numerix import array, pi
894 self._data = scan
[1446]895 self._outfile = outfile
[1457]896 dates = self._data.get_time(asdatetime=True)
[1389]897 t = PL.date2num(dates)
898 tz = timezone('UTC')
899 PL.cla()
[1446]900 #PL.ioff()
[1389]901 PL.clf()
[1723]902 # Adjust subplot layouts
903 if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
904 lef, bot, rig, top, wsp, hsp = self._panellayout
905 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
906 wspace=wsp,hspace=hsp)
907
[1389]908 tdel = max(t) - min(t)
909 ax = PL.subplot(2,1,1)
910 el = array(self._data.get_elevation())*180./pi
911 PL.ylabel('El [deg.]')
912 dstr = dates[0].strftime('%Y/%m/%d')
913 if tdel > 1.0:
914 dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
915 dstr = dstr + " - " + dstr2
916 majloc = DayLocator()
917 minloc = HourLocator(range(0,23,12))
918 timefmt = DateFormatter("%b%d")
919 else:
920 timefmt = DateFormatter('%H')
921 majloc = HourLocator()
922 minloc = MinuteLocator(20)
923 PL.title(dstr)
[1446]924
925 if tdel == 0.0:
926 th = (t - PL.floor(t))*24.0
927 PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
928 else:
929 PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
930 #ax.grid(True)
931 ax.xaxis.set_major_formatter(timefmt)
932 ax.xaxis.set_major_locator(majloc)
933 ax.xaxis.set_minor_locator(minloc)
[1389]934 ax.yaxis.grid(True)
935 yloc = MultipleLocator(30)
936 ax.set_ylim(0,90)
937 ax.yaxis.set_major_locator(yloc)
938 if tdel > 1.0:
939 labels = ax.get_xticklabels()
940 # PL.setp(labels, fontsize=10, rotation=45)
941 PL.setp(labels, fontsize=10)
[1446]942
[1389]943 # Az plot
944 az = array(self._data.get_azimuth())*180./pi
945 if min(az) < 0:
946 for irow in range(len(az)):
947 if az[irow] < 0: az[irow] += 360.0
948
949 ax = PL.subplot(2,1,2)
[1446]950 #PL.xlabel('Time (UT [hour])')
[1389]951 PL.ylabel('Az [deg.]')
[1446]952 if tdel == 0.0:
953 PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
954 else:
955 PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
956 ax.xaxis.set_major_formatter(timefmt)
957 ax.xaxis.set_major_locator(majloc)
958 ax.xaxis.set_minor_locator(minloc)
959 #ax.grid(True)
[1389]960 ax.set_ylim(0,360)
961 ax.yaxis.grid(True)
962 #hfmt = DateFormatter('%H')
963 #hloc = HourLocator()
964 yloc = MultipleLocator(60)
965 ax.yaxis.set_major_locator(yloc)
966 if tdel > 1.0:
967 labels = ax.get_xticklabels()
968 PL.setp(labels, fontsize=10)
[1446]969 PL.xlabel('Time (UT [day])')
970 else:
971 PL.xlabel('Time (UT [hour])')
972
973 #PL.ion()
[1389]974 PL.draw()
[1446]975 if (self._outfile is not None):
976 PL.savefig(self._outfile)
[1389]977
[1446]978 def plotpointing(self, scan=None, outfile=None):
[1389]979 """
980 plot telescope pointings
981 """
982 import pylab as PL
983 from matplotlib.dates import DateFormatter, timezone
984 from matplotlib.ticker import MultipleLocator
985 from matplotlib.numerix import array, pi, zeros
986 self._data = scan
[1446]987 self._outfile = outfile
[1389]988 dir = array(self._data.get_directionval()).transpose()
989 ra = dir[0]*180./pi
990 dec = dir[1]*180./pi
991 PL.cla()
[1446]992 #PL.ioff()
[1389]993 PL.clf()
[1723]994 # Adjust subplot layouts
995 if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
996 lef, bot, rig, top, wsp, hsp = self._panellayout
997 PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
998 wspace=wsp,hspace=hsp)
[1389]999 ax = PL.axes([0.1,0.1,0.8,0.8])
1000 ax = PL.axes([0.1,0.1,0.8,0.8])
1001 ax.set_aspect('equal')
1002 PL.plot(ra,dec, 'b,')
1003 PL.xlabel('RA [deg.]')
1004 PL.ylabel('Declination [deg.]')
1005 PL.title('Telescope pointings')
1006 [xmin,xmax,ymin,ymax] = PL.axis()
1007 PL.axis([xmax,xmin,ymin,ymax])
[1446]1008 #PL.ion()
[1389]1009 PL.draw()
[1446]1010 if (self._outfile is not None):
1011 PL.savefig(self._outfile)
[1389]1012
[1446]1013 # plot total power data
1014 # plotting in time is not yet implemented..
1015 def plottp(self, scan=None, outfile=None):
1016 if self._plotter.is_dead:
1017 self._plotter = self._newplotter()
1018 self._plotter.hold()
1019 self._plotter.clear()
1020 from asap import scantable
1021 if not self._data and not scan:
1022 msg = "Input is not a scantable"
1023 if rcParams['verbose']:
[1612]1024 #print msg
[1614]1025 asaplog.push( msg )
1026 print_log( 'ERROR' )
[1446]1027 return
1028 raise TypeError(msg)
1029 if isinstance(scan, scantable):
1030 if self._data is not None:
1031 if scan != self._data:
1032 self._data = scan
1033 # reset
1034 self._reset()
1035 else:
1036 self._data = scan
1037 self._reset()
1038 # ranges become invalid when abcissa changes?
1039 #if self._abcunit and self._abcunit != self._data.get_unit():
1040 # self._minmaxx = None
1041 # self._minmaxy = None
1042 # self._abcunit = self._data.get_unit()
1043 # self._datamask = None
[1723]1044
1045 # Adjust subplot layouts
1046 if len(self._panellayout) !=6: self.set_panellayout(refresh=False)
1047 lef, bot, rig, top, wsp, hsp = self._panellayout
1048 self._plotter.figure.subplots_adjust(
1049 left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
[1724]1050 if self._plotter.figmgr.casabar: self._plotter.figmgr.casabar.disable_button()
[1446]1051 self._plottp(self._data)
1052 if self._minmaxy is not None:
1053 self._plotter.set_limits(ylim=self._minmaxy)
1054 self._plotter.release()
1055 self._plotter.tidy()
1056 self._plotter.show(hardrefresh=False)
1057 print_log()
1058 return
1059
1060 def _plottp(self,scan):
1061 """
1062 private method for plotting total power data
1063 """
1064 from matplotlib.numerix import ma, array, arange, logical_not
1065 r=0
1066 nr = scan.nrow()
1067 a0,b0 = -1,-1
1068 allxlim = []
1069 allylim = []
1070 y=[]
1071 self._plotter.set_panels()
1072 self._plotter.palette(0)
1073 #title
1074 #xlab = self._abcissa and self._abcissa[panelcount] \
1075 # or scan._getabcissalabel()
1076 #ylab = self._ordinate and self._ordinate[panelcount] \
1077 # or scan._get_ordinate_label()
1078 xlab = self._abcissa or 'row number' #or Time
1079 ylab = self._ordinate or scan._get_ordinate_label()
1080 self._plotter.set_axes('xlabel',xlab)
1081 self._plotter.set_axes('ylabel',ylab)
1082 lbl = self._get_label(scan, r, 's', self._title)
1083 if isinstance(lbl, list) or isinstance(lbl, tuple):
1084 # if 0 <= panelcount < len(lbl):
1085 # lbl = lbl[panelcount]
1086 # else:
1087 # get default label
1088 lbl = self._get_label(scan, r, self._panelling, None)
1089 self._plotter.set_axes('title',lbl)
1090 y=array(scan._get_column(scan._getspectrum,-1))
1091 m = array(scan._get_column(scan._getmask,-1))
1092 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
1093 x = arange(len(y))
1094 # try to handle spectral data somewhat...
1095 l,m = y.shape
1096 if m > 1:
1097 y=y.mean(axis=1)
1098 plotit = self._plotter.plot
1099 llbl = self._get_label(scan, r, self._stacking, None)
1100 self._plotter.set_line(label=llbl)
1101 if len(x) > 0:
1102 plotit(x,y)
1103
1104
1105 # forwards to matplotlib.Figure.text
1106 def figtext(self, *args, **kwargs):
1107 """
1108 Add text to figure at location x,y (relative 0-1 coords).
1109 This method forwards *args and **kwargs to a Matplotlib method,
1110 matplotlib.Figure.text.
1111 See the method help for detailed information.
1112 """
1113 self._plotter.text(*args, **kwargs)
1114 # end matplotlib.Figure.text forwarding function
1115
[1723]1116
1117 # printing header information
1118 def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
1119 """
1120 print data (scantable) header on the plot and/or logger.
1121 Parameters:
1122 plot: whether or not print header info on the plot.
1123 fontsize: header font size (valid only plot=True)
1124 autoscale: whether or not autoscale the plot (valid only plot=True)
1125 logger: whether or not print header info on the logger.
1126 selstr: additional selection string (not verified)
1127 extrastr: additional string to print (not verified)
1128 """
1129 if not plot and not logger: return
1130 if not self._data: raise RuntimeError("No scantable has been set yet.")
1131 # Now header will be printed on plot and/or logger.
1132 # Get header information and format it.
1133 ssum=self._data.__str__()
1134 # Print Observation header to the upper-left corner of plot
1135 if plot:
1136 srest=ssum[ssum.find('Rest Freqs:'):ssum.find('Abcissa:')]
1137 shead=ssum[ssum.find('Beams:'):ssum.find('Flux Unit:')]
1138 headstr=shead.split('\n\n')
1139 if extrastr != '': headstr[1]=extrastr+'\n'+headstr[1]
1140 #headstr[1]='Data File: '+(filestr or 'unknown')+'\n'+headstr[1]
1141 headstr[0]=headstr[0]+'\n'+srest
1142 headstr.reverse()
1143 ssel='***Selections***\n'+(selstr+self._data.get_selection().__str__() or 'none')
1144 headstr.append(ssel)
1145 nstcol=len(headstr)
1146
1147 self._plotter.hold()
1148 for i in range(nstcol):
1149 self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
1150 headstr[i],
1151 horizontalalignment='left',
1152 verticalalignment='top',
1153 fontsize=fontsize)
1154 import time
1155 self._plotter.figure.text(0.99,0.0,
1156 time.strftime("%a %d %b %Y %H:%M:%S %Z"),
1157 horizontalalignment='right',
1158 verticalalignment='bottom',fontsize=8)
1159 self._plotter.release()
1160 del srest, shead, headstr, ssel
1161 if logger:
1162 asaplog.push("----------------\n Plot Summary\n----------------")
1163 asaplog.push(extrastr)
1164 asaplog.push(ssum[ssum.find('Beams:'):])
1165 print_log()
1166 del ssum
[1724]1167
1168
Note: See TracBrowser for help on using the repository browser.