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

Last change on this file since 1723 was 1723, checked in by Kana Sugimoto, 14 years ago

New Development: No

JIRA Issue: Yes (CAS-1801, CAS-2064)

Ready to Release: Yes

Interface Changes: Yes

What Interface Changed:
Three methods, print_headder(), set_panellayout() and _reset_panellayout()
are added to the class asapplotter.
A parameter layout (list) is added to set_layout() in asaplotbase class.

Test Programs: run sdplot with header & plotstyle=True + layout parameter set.

Put in Release Notes: Yes

Module(s): sdplot() task.

Description:

  1. asapplotter.print_header(plot=True, fontsize=9, logger=False, selstr=, extrastr=): prints scantable header on the plot and/or logger (see help doc for parameter details). Note that the header information will not overlayed to the plot by plotazel() and plotpointing().
  2. set_panellayout(layout=[],refresh=True): sets the layout of subplots. layout is a list of subplots layout in figure coordinate (0-1) in order of [left, bottom, right, top, horizontal space btw panels, vertical space btw panels]
  3. _reset_panellayout(): resets the layout of subplots to matplotlib rc value.
  4. a member variable 'asapplotter._panellayout' (list) is specified to store layout values.


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