source: trunk/python/asapplotter.py @ 1739

Last change on this file since 1739 was 1739, checked in by Malte Marquarding, 14 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
RevLine 
[1695]1from asap import rcParams, print_log, print_log_dec
2from asap import selector, scantable
[1153]3import matplotlib.axes
[1556]4from matplotlib.font_manager import FontProperties
5from matplotlib.text import Text
6
[1317]7import re
[203]8
9class asapplotter:
[226]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    """
[1563]18    def __init__(self, visible=None , **kwargs):
[734]19        self._visible = rcParams['plotter.gui']
20        if visible is not None:
21            self._visible = visible
[1563]22        self._plotter = self._newplotter(**kwargs)
[710]23
[554]24        self._panelling = None
25        self._stacking = None
26        self.set_panelling()
27        self.set_stacking()
[377]28        self._rows = None
29        self._cols = None
[203]30        self._autoplot = False
[525]31        self._minmaxx = None
32        self._minmaxy = None
[710]33        self._datamask = None
[203]34        self._data = None
[607]35        self._lmap = None
[226]36        self._title = None
[257]37        self._ordinate = None
38        self._abcissa = None
[709]39        self._abcunit = None
[920]40        self._usermask = []
41        self._maskselection = None
42        self._selection = selector()
[1023]43        self._hist = rcParams['plotter.histogram']
[1556]44        self._fp = FontProperties()
[1023]45
[920]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
[1563]54    def _newplotter(self, **kwargs):
[710]55        if self._visible:
56            from asap.asaplotgui import asaplotgui as asaplot
57        else:
58            from asap.asaplot import asaplot
[1563]59        return asaplot(**kwargs)
[710]60
[1695]61    @print_log_dec
[935]62    def plot(self, scan=None):
[203]63        """
[920]64        Plot a scantable.
[203]65        Parameters:
[920]66            scan:   a scantable
[203]67        Note:
[920]68            If a scantable was specified in a previous call
[203]69            to plot, no argument has to be given to 'replot'
[920]70            NO checking is done that the abcissas of the scantable
[203]71            are consistent e.g. all 'channel' or all 'velocity' etc.
72        """
[710]73        if self._plotter.is_dead:
74            self._plotter = self._newplotter()
[600]75        self._plotter.hold()
[203]76        self._plotter.clear()
[920]77        from asap import scantable
[935]78        if not self._data and not scan:
[1101]79            msg = "Input is not a scantable"
80            if rcParams['verbose']:
81                print msg
82                return
83            raise TypeError(msg)
[920]84        if isinstance(scan, scantable):
[709]85            if self._data is not None:
[920]86                if scan != self._data:
87                    self._data = scan
[710]88                    # reset
89                    self._reset()
[525]90            else:
[920]91                self._data = scan
[710]92                self._reset()
[709]93        # ranges become invalid when unit changes
[935]94        if self._abcunit and self._abcunit != self._data.get_unit():
[709]95            self._minmaxx = None
96            self._minmaxy = None
[920]97            self._abcunit = self._data.get_unit()
[710]98            self._datamask = None
[920]99        self._plot(self._data)
[709]100        if self._minmaxy is not None:
101            self._plotter.set_limits(ylim=self._minmaxy)
[203]102        self._plotter.release()
[1153]103        self._plotter.tidy()
104        self._plotter.show(hardrefresh=False)
[203]105        return
106
[1572]107    def gca(self):
108        return self._plotter.figure.gca()
109
[1550]110    def refresh(self):
[1572]111        """Do a soft refresh"""
[1550]112        self._plotter.figure.show()
113
[1555]114    def create_mask(self, nwin=1, panel=0, color=None):
[1597]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        """
[1555]124        if self._data is None:
125            return []
[1547]126        outmask = []
[1549]127        self._plotter.subplot(panel)
128        xmin, xmax = self._plotter.axes.get_xlim()
[1548]129        marg = 0.05*(xmax-xmin)
[1549]130        self._plotter.axes.set_xlim(xmin-marg, xmax+marg)
[1550]131        self.refresh()
[1695]132
[1555]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):
[1547]142            wpos = []
[1695]143            self.text(0.05,1.0, "Add start boundary",
[1555]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])
[1695]150            self.axvline(wpos[0], color=color)
[1551]151            self.text(0.05,1.0, "Add end boundary", coords="relative", fontsize=10)
[1555]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()
[1547]161            outmask.append(wpos)
[1153]162
[1555]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
[1153]169    # forwards to matplotlib axes
170    def text(self, *args, **kwargs):
[1547]171        if kwargs.has_key("interactive"):
172            if kwargs.pop("interactive"):
173                pos = self._plotter.get_point()
174                args = tuple(pos)+args
[1153]175        self._axes_callback("text", *args, **kwargs)
[1547]176
[1358]177    text.__doc__ = matplotlib.axes.Axes.text.__doc__
[1559]178
[1153]179    def arrow(self, *args, **kwargs):
[1547]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
[1153]187        self._axes_callback("arrow", *args, **kwargs)
[1547]188
[1358]189    arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
[1559]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
[1153]202    def axvline(self, *args, **kwargs):
[1547]203        if kwargs.has_key("interactive"):
204            if kwargs.pop("interactive"):
205                pos = self._plotter.get_point()
206                args = (pos[0],)+args
[1153]207        self._axes_callback("axvline", *args, **kwargs)
[1559]208
[1358]209    axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
[1547]210
[1153]211    def axhline(self, *args, **kwargs):
[1547]212        if kwargs.has_key("interactive"):
213            if kwargs.pop("interactive"):
214                pos = self._plotter.get_point()
215                args = (pos[1],)+args
[1153]216        self._axes_callback("axhline", *args, **kwargs)
[1559]217
[1358]218    axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
[1547]219
[1153]220    def axvspan(self, *args, **kwargs):
[1547]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
[1153]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???
[1547]230        #del self._plotter.axes.patches[-1]
231
[1358]232    axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
[1232]233
[1153]234    def axhspan(self, *args, **kwargs):
[1547]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
[1232]241        self._axes_callback("axhspan", *args, **kwargs)
[1153]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???
[1547]245        #del self._plotter.axes.patches[-1]
[1559]246
[1358]247    axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
[1153]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
[1547]267
[226]268    def set_mode(self, stacking=None, panelling=None):
[203]269        """
[377]270        Set the plots look and feel, i.e. what you want to see on the plot.
[203]271        Parameters:
272            stacking:     tell the plotter which variable to plot
[1217]273                          as line colour overlays (default 'pol')
[203]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        """
[753]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)
[920]292        if self._data: self.plot(self._data)
[203]293        return
294
[554]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)
[203]300        if md:
[554]301            self._panelling = md
[226]302            self._title = None
[203]303            return True
304        return False
305
[377]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
[920]319        if self._data: self.plot(self._data)
[377]320        return
321
[709]322    def set_stacking(self, what=None):
[554]323        mode = what
[709]324        if mode is None:
325             mode = rcParams['plotter.stacking']
[554]326        md = self._translate(mode)
[203]327        if md:
328            self._stacking = md
[226]329            self._lmap = None
[203]330            return True
331        return False
332
[525]333    def set_range(self,xstart=None,xend=None,ystart=None,yend=None):
[203]334        """
335        Set the range of interest on the abcissa of the plot
336        Parameters:
[525]337            [x,y]start,[x,y]end:  The start and end points of the 'zoom' window
[203]338        Note:
339            These become non-sensical when the unit changes.
340            use plotter.set_range() without parameters to reset
341
342        """
[525]343        if xstart is None and xend is None:
344            self._minmaxx = None
[600]345        else:
346            self._minmaxx = [xstart,xend]
[525]347        if ystart is None and yend is None:
348            self._minmaxy = None
[600]349        else:
[709]350            self._minmaxy = [ystart,yend]
[920]351        if self._data: self.plot(self._data)
[203]352        return
[709]353
[1101]354    def set_legend(self, mp=None, fontsize = None, mode = 0):
[203]355        """
356        Specify a mapping for the legend instead of using the default
357        indices:
358        Parameters:
[1101]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:
[1096]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
[203]378
379        Example:
[485]380             If the data has two IFs/rest frequencies with index 0 and 1
[203]381             for CO and SiO:
382             plotter.set_stacking('i')
[710]383             plotter.set_legend(['CO','SiO'])
[203]384             plotter.plot()
[710]385             plotter.set_legend([r'$^{12}CO$', r'SiO'])
[203]386        """
387        self._lmap = mp
[1096]388        self._plotter.legend(mode)
[1101]389        if isinstance(fontsize, int):
390            from matplotlib import rc as rcp
391            rcp('legend', fontsize=fontsize)
[1096]392        if self._data:
393            self.plot(self._data)
[226]394        return
395
[1101]396    def set_title(self, title=None, fontsize=None):
[710]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        """
[226]404        self._title = title
[1101]405        if isinstance(fontsize, int):
406            from matplotlib import rc as rcp
407            rcp('axes', titlesize=fontsize)
[920]408        if self._data: self.plot(self._data)
[226]409        return
410
[1101]411    def set_ordinate(self, ordinate=None, fontsize=None):
[710]412        """
413        Set the y-axis label of the plot. If multiple panels are plotted,
414        multiple labels have to be specified.
[1021]415        Parameters:
416            ordinate:    a list of ordinate labels. None (default) let
417                         data determine the labels
[710]418        Example:
419             # two panels are visible on the plotter
420             plotter.set_ordinate(["First Y-Axis","Second Y-Axis"])
421        """
[257]422        self._ordinate = ordinate
[1101]423        if isinstance(fontsize, int):
424            from matplotlib import rc as rcp
425            rcp('axes', labelsize=fontsize)
426            rcp('ytick', labelsize=fontsize)
[920]427        if self._data: self.plot(self._data)
[257]428        return
429
[1101]430    def set_abcissa(self, abcissa=None, fontsize=None):
[710]431        """
432        Set the x-axis label of the plot. If multiple panels are plotted,
433        multiple labels have to be specified.
[1021]434        Parameters:
435            abcissa:     a list of abcissa labels. None (default) let
436                         data determine the labels
[710]437        Example:
438             # two panels are visible on the plotter
439             plotter.set_ordinate(["First X-Axis","Second X-Axis"])
440        """
[257]441        self._abcissa = abcissa
[1101]442        if isinstance(fontsize, int):
443            from matplotlib import rc as rcp
444            rcp('axes', labelsize=fontsize)
445            rcp('xtick', labelsize=fontsize)
[920]446        if self._data: self.plot(self._data)
[257]447        return
448
[1217]449    def set_colors(self, colmap):
[377]450        """
[1217]451        Set the colours to be used. The plotter will cycle through
452        these colours when lines are overlaid (stacking mode).
[1021]453        Parameters:
[1217]454            colmap:     a list of colour names
[710]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        """
[1217]461        if isinstance(colmap,str):
462            colmap = colmap.split()
463        self._plotter.palette(0, colormap=colmap)
[920]464        if self._data: self.plot(self._data)
[710]465
[1217]466    # alias for english speakers
467    set_colours = set_colors
468
[1101]469    def set_histogram(self, hist=True, linewidth=None):
[1021]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        """
[1023]477        self._hist = hist
[1101]478        if isinstance(linewidth, float) or isinstance(linewidth, int):
479            from matplotlib import rc as rcp
480            rcp('lines', linewidth=linewidth)
[1021]481        if self._data: self.plot(self._data)
[1023]482
[1101]483    def set_linestyles(self, linestyles=None, linewidth=None):
[710]484        """
[734]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.
[710]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)
[1101]504        if isinstance(linewidth, float) or isinstance(linewidth, int):
505            from matplotlib import rc as rcp
506            rcp('lines', linewidth=linewidth)
[920]507        if self._data: self.plot(self._data)
[710]508
[1547]509    def set_font(self, **kwargs):
[1101]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
[1547]520        fdict = {}
521        for k,v in kwargs.iteritems():
522            if v:
523                fdict[k] = v
[1556]524        self._fp = FontProperties(**fdict)
[1547]525        if self._data:
[1556]526            self.plot()
[1101]527
[1259]528    def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
[1146]529                   location=None):
530        """
[1158]531        Plot a line catalog.
532        Parameters:
533            linecat:      the linecatalog to plot
[1168]534            doppler:      the velocity shift to apply to the frequencies
[1158]535            deltachan:    the number of channels to include each side of the
536                          line to determine a local maximum/minimum
[1259]537            rotate:       the rotation (in degrees) )for the text label (default 90.0)
[1158]538            location:     the location of the line annotation from the 'top',
539                          'bottom' or alternate (None - the default)
[1165]540        Notes:
541        If the spectrum is flagged no line will be drawn in that location.
[1146]542        """
[1259]543        if not self._data:
544            raise RuntimeError("No scantable has been plotted yet.")
[1146]545        from asap._asap import linecatalog
[1259]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.")
[1739]550        from numpy import ma
[1146]551        for j in range(len(self._plotter.subplots)):
552            self._plotter.subplot(j)
553            lims = self._plotter.axes.get_xlim()
[1153]554            for row in range(linecat.nrow()):
[1259]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()]
[1165]558                c = 299792.458
[1174]559                freq = restf*(1.0-doppler/c)
[1146]560                if lims[0] < freq < lims[1]:
561                    if location is None:
562                        loc = 'bottom'
[1153]563                        if row%2: loc='top'
[1146]564                    else: loc = location
[1153]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)
[1167]589                            y = line._y[s]
[1165]590                            maxy = ma.maximum(y)
591                            if isinstance( maxy, float):
592                                maxys.append(maxy)
[1164]593                    if len(maxys):
594                        peak = max(maxys)
[1165]595                        if peak > self._plotter.axes.get_ylim()[1]:
596                            loc = 'bottom'
[1164]597                    else:
598                        continue
[1157]599                    self._plotter.vline_with_label(freq, peak,
600                                                   linecat.get_name(row),
601                                                   location=loc, rotate=rotate)
[1153]602        self._plotter.show(hardrefresh=False)
[1146]603
[1153]604
[710]605    def save(self, filename=None, orientation=None, dpi=None):
606        """
[377]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.
[709]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.
[710]618             dpi:         The dpi of the output non-ps plot
[377]619        """
[709]620        self._plotter.save(filename,orientation,dpi)
[377]621        return
[709]622
[257]623
[920]624    def set_mask(self, mask=None, selection=None):
[525]625        """
[734]626        Set a plotting mask for a specific polarization.
627        This is useful for masking out "noise" Pangle outside a source.
628        Parameters:
[920]629             mask:           a mask from scantable.create_mask
630             selection:      the spectra to apply the mask to.
[734]631        Example:
[920]632             select = selector()
633             select.setpolstrings("Pangle")
634             plotter.set_mask(mymask, select)
[734]635        """
[710]636        if not self._data:
[920]637            msg = "Can only set mask after a first call to plot()"
[753]638            if rcParams['verbose']:
639                print msg
[762]640                return
[753]641            else:
[762]642                raise RuntimeError(msg)
[920]643        if len(mask):
644            if isinstance(mask, list) or isinstance(mask, tuple):
645                self._usermask = array(mask)
[710]646            else:
[920]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):
[947]652            self._maskselection = {'b': selection.get_beams(),
653                                   's': selection.get_scans(),
654                                   'i': selection.get_ifs(),
655                                   'p': selection.get_pols(),
[920]656                                   't': [] }
[710]657        else:
[920]658            self._maskselection = None
659        self.plot(self._data)
[710]660
[709]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
[1101]673        while start > 0 and data[start] < mn:
[709]674            start+= inc
675        # find max index
[1101]676        while end > 0 and data[end] > mx:
[709]677            end-=inc
[1101]678        if end > 0: end +=1
[709]679        if start > end:
680            return end,start
681        return start,end
682
[710]683    def _reset(self):
[920]684        self._usermask = []
[710]685        self._usermaskspectra = None
[920]686        self.set_selection(None, False)
687
688    def _plot(self, scan):
[947]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)
[920]696        d = {'b': scan.getbeam, 's': scan.getscan,
697             'i': scan.getif, 'p': scan.getpol, 't': scan._gettime }
698
[1148]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
[1175]705        else: n = len(n0)
[1148]706        if isinstance(nstack0, int): nstack = nstack0
[1175]707        else: nstack = len(nstack0)
[1582]708        maxpanel, maxstack = 16,16
[920]709        if n > maxpanel or nstack > maxstack:
710            from asap import asaplog
[1148]711            maxn = 0
712            if nstack > maxstack: maxn = maxstack
713            if n > maxpanel: maxn = maxpanel
[920]714            msg ="Scan to be plotted contains more than %d selections.\n" \
[1148]715                  "Selecting first %d selections..." % (maxn, maxn)
[920]716            asaplog.push(msg)
717            print_log()
718            n = min(n,maxpanel)
[998]719            nstack = min(nstack,maxstack)
[920]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 = []
[1018]734        allylim = []
[920]735        newpanel=True
736        panelcount,stackcount = 0,0
[1002]737        while r < nr:
[920]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()
[1547]749                self._plotter.set_axes('xlabel', xlab)
750                self._plotter.set_axes('ylabel', ylab)
[920]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)
[1739]769                from numpy import logical_not, logical_and
[920]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)
[1739]774                from numpy import ma, array
[1116]775                y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
[920]776                if self._minmaxx is not None:
777                    s,e = self._slice_indeces(x)
778                    x = x[s:e]
779                    y = y[s:e]
[1096]780                if len(x) > 1024 and rcParams['plotter.decimate']:
781                    fac = len(x)/1024
[920]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)
[1023]793                plotit = self._plotter.plot
794                if self._hist: plotit = self._plotter.hist
[1146]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
[920]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):
[1018]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]])
[998]818                break
[920]819            r+=1 # next row
[947]820        #reset the selector to the scantable's original
821        scan.set_selection(savesel)
[1556]822        if self._fp is not None:
823            for o in self._plotter.figure.findobj(Text):
824                o.set_fontproperties(self._fp)
[920]825
[1556]826
[1582]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
[920]843        d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
844              'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME' }
845        order = [d0[self._panelling],d0[self._stacking]]
[947]846        self._selection.set_order(order)
[920]847        if self._data and refresh: self.plot(self._data)
848
849    def _get_selected_n(self, scan):
[1148]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() }
[920]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):
[1153]862        if isinstance(userlabel, list) and len(userlabel) == 0:
863            userlabel = " "
[947]864        pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
[920]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)),
[964]872             'p': poleval,
[1175]873             't': str(scan.get_time(row)) }
[920]874        return userlabel or d[mode]
[1153]875
[1556]876    def plotazel(self):
[1391]877        """
[1696]878        plot azimuth and elevation versus time of a scantable
[1391]879        """
[1696]880        from matplotlib import pylab as PL
881        from matplotlib.dates import DateFormatter, timezone
882        from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
[1391]883        from matplotlib.ticker import MultipleLocator
[1739]884        from numpy import array, pi
[1556]885        dates = self._data.get_time(asdatetime=True)
[1391]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")
[1696]902        elif tdel > 24./60.:
903            timefmt = DateFormatter('%H:%M')
904            majloc = HourLocator()
905            minloc = MinuteLocator(30)
[1391]906        else:
[1696]907            timefmt = DateFormatter('%H:%M')
908            majloc = MinuteLocator(interval=5)
909            minloc = SecondLocator(30)
910
[1391]911        PL.title(dstr)
912        PL.plot_date(t,el,'b,', tz=tz)
913        ax.yaxis.grid(True)
[1696]914
[1391]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
[1696]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
[1391]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
[1556]937    def plotpointing(self):
[1391]938        """
939        plot telescope pointings
940        """
[1696]941        from matplotlib import pylab as PL
[1739]942        from numpy import array
[1391]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')
[1696]952        PL.plot(ra, dec, 'b,')
[1391]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.