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

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

New Development: No

JIRA Issue: Yes (CAS-1077)

Ready to Release: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s):

Description: Add a note to help message and log output for set_data().

Just for user infomation.


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