source: trunk/python/asapplotter.py@ 1792

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

Replace matplotlib.numerix with numpy

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