source: trunk/python/asapplotter.py@ 1561

Last change on this file since 1561 was 1559, checked in by Malte Marquarding, 15 years ago

added forward for axes.annotate and added useful default plus interactive mode

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