source: trunk/python/asapplotter.py@ 1548

Last change on this file since 1548 was 1548, checked in by Malte Marquarding, 16 years ago

add interactive annotation capability. Also allow creation of masks interactively

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