casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
interactivemask.py
Go to the documentation of this file.
00001 from asap.parameters import rcParams
00002 from asap.utils import _n_bools, mask_and, mask_or
00003 from asap.scantable import scantable
00004 from asap.logging import asaplog, asaplog_post_dec
00005 
00006 class interactivemask:
00007     """
00008     The class for interactive mask selection.
00009 
00010     Example:
00011        my_mask=interactivemask(plotter,scan)
00012        my_mask.set_basemask(masklist=[[0,10],[90,100]],invert=False)
00013        # Do interactive mask selection
00014        my_mask.select_mask()
00015        finish=raw_input('Press return to finish selection.\n')
00016        my_mask.finish_selection(callback=func)
00017        mask=my_mask.get_mask()
00018 
00019     Modify mask region by selecting a region on a plot with mouse.
00020     """
00021 
00022     def __init__(self,plotter=None, scan=None):
00023         """
00024         Create a interactive masking object.
00025         Either or both 'plotter' or/and 'scan' should be defined.
00026 
00027         Parameters:
00028            plotter: an ASAP plotter object for interactive selection
00029            scan: a scantable to create a mask interactively
00030         """
00031         # Return if GUI is not active
00032         if not rcParams['plotter.gui']:
00033             msg = 'GUI plotter is disabled.\n'
00034             msg += 'Exit interactive mode.'
00035             asaplog.push(msg)
00036             asaplog.post("ERROR")
00037             return
00038         # Verify input parameters
00039         if scan is None and plotter is None:
00040             msg = "Either scantable or plotter should be defined."
00041             raise TypeError(msg)
00042 
00043         self.scan = None
00044         self.p = None
00045         self.newplot = False
00046         if scan and isinstance(scan, scantable):
00047             self.scan = scan
00048         from asap.asapplotter import asapplotter
00049         if plotter and isinstance(plotter,asapplotter):
00050             self.p = plotter
00051             if self.scan is None and isinstance(self.p._data,scantable):
00052                 self.scan = self.p._data
00053         if self.scan is None:
00054             msg = "Invalid scantable."
00055             raise TypeError(msg)
00056 
00057         self.mask = _n_bools(self.scan.nchan(self.scan.getif(0)),True)
00058         self.callback = None
00059         self.event = None
00060         self.once = False
00061         self.showmask = True
00062         self.rect = {}
00063         self.xold = None
00064         self.yold = None
00065         self.xdataold = None
00066         self.ydataold = None
00067         self._polygons = []
00068 
00069 
00070     def set_basemask(self,masklist=[],invert=False):
00071         """
00072         Set initial channel mask.
00073 
00074         Parameters:
00075             masklist:  [[min, max], [min2, max2], ...]
00076                        A list of pairs of start/end points (inclusive)
00077                                specifying the regions to be masked
00078             invert:    optional argument. If specified as True,
00079                        return an inverted mask, i.e. the regions
00080                    specified are excluded
00081         You can reset the mask selection by running this method with
00082         the default parameters.
00083         """
00084         # Verify input parameters
00085         if not (isinstance(masklist, list) or isinstance(masklist, tuple)) \
00086            or not isinstance(invert, bool):
00087             msg = 'Invalid mask definition'
00088             raise TypeError(msg)
00089 
00090         # Create base mask
00091         if ( len(masklist) > 0 ):
00092             self.mask = self.scan.create_mask(masklist,invert=invert)
00093         elif invert == True:
00094             self.mask = _n_bools(self.scan.nchan(self.scan.getif(0)),False)
00095         else:
00096             self.mask = _n_bools(self.scan.nchan(self.scan.getif(0)),True)
00097 
00098 
00099     def set_startevent(self,event):
00100         """
00101         Inherit an event from the parent function.
00102 
00103         Parameters:
00104             event: 'button_press_event' object to be inherited to
00105                    start interactive region selection .
00106         """
00107         from matplotlib.backend_bases import MouseEvent
00108         if isinstance(event,MouseEvent) and event.name == 'button_press_event':
00109             self.event = event
00110         else:
00111             msg = "Invalid event."
00112             raise TypeError(msg)
00113 
00114     def set_callback(self,callback):
00115         """
00116         Set callback function to run when finish_selection() is executed.
00117             callback: The post processing function to run after
00118                       the mask selections are completed.
00119                   This will be overwritten if callback is defined in
00120                   finish_selection(callback=func)
00121         """
00122         self.callback = callback
00123 
00124     def select_mask(self,once=False,showmask=True):
00125         """
00126         Do interactive mask selection.
00127         Modify masks interactively by adding/deleting regions with
00128         mouse drawing.(left-button: mask; right-button: UNmask)
00129         Note that the interactive region selection is available only
00130         when GUI plotter is active.
00131 
00132         Parameters:
00133             once:     If specified as True, you can modify masks only
00134                       once. Else if False, you can modify them repeatedly.
00135             showmask: If specified as True, the masked regions are plotted
00136                       on the plotter.
00137                   Note this parameter is valid only when once=True.
00138                   Otherwise, maskes are forced to be plotted for reference.
00139         """
00140         # Return if GUI is not active
00141         if not rcParams['plotter.gui']:
00142             msg = 'GUI plotter is disabled.\n'
00143             msg += 'Exit interactive mode.'
00144             asaplog.push(msg)
00145             asaplog.post("ERROR")
00146             return
00147 
00148         self.once = once
00149         if self.once:
00150             self.showmask = showmask
00151         else:
00152             if not showmask:
00153                 asaplog.post()
00154                 asaplog.push('showmask spcification is ignored. Mask regions are plotted anyway.')
00155                 asaplog.post("WARN")
00156             self.showmask = True
00157 
00158         if not self.p:
00159             asaplog.push('A new ASAP plotter will be loaded')
00160             asaplog.post()
00161             from asap.asapplotter import asapplotter
00162             self.p = asapplotter()
00163             self.newplot = True
00164         self.p._assert_plotter(action='reload')
00165         from matplotlib import rc as rcp
00166         rcp('lines', linewidth=1)
00167         
00168         # Plot selected spectra if needed
00169         if self.scan != self.p._data:
00170             if len(self.scan.getifnos()) > 16:
00171                 asaplog.post()
00172                 asaplog.push("Number of panels > 16. Plotting the first 16...")
00173                 asaplog.post("WARN")
00174             # Need replot
00175             self.p._legendloc = 1
00176             self.p.plot(self.scan)
00177             # disable casa toolbar
00178             if self.p._plotter.figmgr.casabar:
00179                 self.p._plotter.figmgr.casabar.disable_button()
00180                 self.p._plotter.figmgr.casabar.disable_prev()
00181                 self.p._plotter.figmgr.casabar.disable_next()
00182             for panel in self.p._plotter.subplots:
00183                 xmin, xmax = panel['axes'].get_xlim()
00184                 marg = 0.05*abs(xmax-xmin)
00185                 panel['axes'].set_xlim(xmin-marg, xmax+marg)
00186                 if rcParams['plotter.ganged']: break
00187             self.p._plotter.show()
00188 
00189         # Plot initial mask region
00190         #if self.showmask or not self.once:
00191         if self.showmask:
00192             self._plot_mask()
00193             print ''
00194             print 'Selected regions are shaded with yellow. (gray: projections)'
00195             print 'Now you can modify the selection.'
00196             print 'Draw rectangles with Left-mouse to add the regions,'
00197             print 'or with Right-mouse to exclude the regions.'
00198 
00199 
00200         if self.event != None:
00201             self._region_start(self.event)
00202         else:
00203             self.p._plotter.register('button_press',None)
00204             self.p._plotter.register('button_press',self._region_start)
00205 
00206 
00207     def _region_start(self,event):
00208         # Do not fire event when in zooming/panning mode
00209         mode = self.p._plotter.figmgr.toolbar.mode
00210         if not mode == '':
00211             return
00212         # Return if selected point is out of panel
00213         if event.inaxes == None: return
00214         # Select mask/unmask region with mask
00215         self.rect = {'button': event.button, 'axes': event.inaxes,
00216                      'x': event.x, 'y': event.y,
00217                      'world': [event.xdata, event.ydata,
00218                                 event.xdata, event.ydata],
00219                      'pixel': [event.x, event.y,
00220                                 event.x, event.y]}
00221         self.p._plotter.register('motion_notify', self._region_draw)
00222         self.p._plotter.register('button_release', self._region_end)
00223 
00224     def _region_draw(self,event):
00225         sameaxes=(event.inaxes == self.rect['axes'])
00226         if sameaxes:
00227             xnow = event.x
00228             ynow = event.y
00229             self.xold = xnow
00230             self.yold = ynow
00231             self.xdataold = event.xdata
00232             self.ydataold = event.ydata
00233         else:
00234             xnow = self.xold
00235             ynow = self.yold
00236 
00237         self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
00238 
00239     def _region_end(self,event):
00240         self.p._plotter.register('motion_notify', None)
00241         self.p._plotter.register('button_release', None)
00242 
00243         # Delete the rubber band
00244         self.p._plotter.figmgr.toolbar.release(event)
00245 
00246         if event.inaxes == self.rect['axes']:
00247             xend = event.x
00248             yend = event.y
00249             xdataend = event.xdata
00250             ydataend = event.ydata
00251         else:
00252             xend = self.xold
00253             yend = self.yold
00254             xdataend = self.xdataold
00255             ydataend = self.ydataold
00256 
00257         self.rect['world'][2:4] = [xdataend, ydataend]
00258         self.rect['pixel'][2:4] = [xend, yend]
00259         self._update_mask()
00260         # Clear up region selection
00261         self.rect = {}
00262         self.xold = None
00263         self.yold = None
00264         self.xdataold = None
00265         self.ydataold = None
00266         if self.once: self.finish_selection(callback=self.callback)
00267 
00268     def _update_mask(self):
00269         # Min and Max for new mask
00270         xstart = self.rect['world'][0]
00271         xend = self.rect['world'][2]
00272         if xstart <= xend: newlist=[xstart,xend]
00273         else: newlist = [xend,xstart]
00274         # Mask or unmask
00275         invmask = None
00276         if self.rect['button'] == 1:
00277             invmask = False
00278             mflg = 'Mask'
00279         elif self.rect['button'] == 3:
00280             invmask = True
00281             mflg = 'UNmask'
00282         asaplog.push(mflg+': '+str(newlist))
00283         asaplog.post()
00284         newmask = self.scan.create_mask(newlist,invert=invmask)
00285         # Logic operation to update mask
00286         if invmask:
00287             self.mask = mask_and(self.mask,newmask)
00288         else:
00289             self.mask = mask_or(self.mask,newmask)
00290         # Plot masked regions
00291         #if self.showmask or not self.once: self._plot_mask()
00292         if self.showmask: self._plot_mask()
00293 
00294     # Plot masked regions
00295     def _plot_mask(self):
00296         msks = []
00297         msks = self.scan.get_masklist(self.mask,row=0)
00298         # Get projection masks for multi-IF
00299         ifs = self.scan.getifnos()
00300         projs = []
00301         if len(ifs) > 1:
00302             row0if = self.scan.getif(0)
00303             for ifno in ifs:
00304                 if ifno == row0if: continue
00305                 for row in xrange(self.scan.nrow()):
00306                     if self.scan.getif(row) == ifno:
00307                         projs.append(self.scan.get_masklist(self.mask,row=row))
00308                         break
00309         if len(self._polygons)>0:
00310             # Remove old polygons
00311             for polygon in self._polygons: polygon.remove()
00312             self._polygons = []
00313         # Plot new polygons
00314         if len(msks) > 0:
00315             npanel = len(self.p._plotter.subplots)
00316             j = -1
00317             for iloop in range(len(msks)*npanel):
00318                 i = iloop % len(msks)
00319                 if  i == 0 : j += 1
00320                 if len(ifs) > 1:
00321                     for k in xrange(len(ifs)-1):
00322                         self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))
00323                 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(msks[i][0],msks[i][1],facecolor='yellow'))
00324         self.p._plotter.canvas.draw()
00325 
00326     def finish_selection(self, callback=None):
00327         """
00328         Execute callback function, reset or close plotter window as
00329         necessary.
00330 
00331         Parameters:
00332             callback: The post processing function to run after
00333                       the mask selections are completed.
00334                   Specifying the callback function here will overwrite
00335                   the one set by set_callback(func)
00336 
00337         Note this function is automatically called at the end of
00338         select_mask() if once=True.
00339         """
00340         if callback: self.callback=callback
00341         if self.callback: self.callback()
00342         if not self.event:
00343             try: self.p._plotter.register('button_press',None)
00344             except: pass # plotter window is closed by X button.
00345         # Finish the plot
00346         if not self.newplot:
00347             self.clear_polygon()
00348         else:
00349             #self.p._plotter.unmap()
00350             self.p._plotter.quit()
00351             self.p._plotter = None
00352             del self.p
00353             self.p = None
00354             self._polygons = []
00355 
00356 
00357     def clear_polygon(self):
00358         """
00359         Erase masks plots from the plotter.
00360         """
00361         if len(self._polygons) > 0:
00362             # Remove old polygons
00363             for polygon in self._polygons: polygon.remove()
00364             self.p._plotter.show()
00365             self._polygons = []
00366 
00367 
00368     def get_mask(self):
00369         """
00370         Get the interactively selected channel mask.
00371         Returns:
00372             A list of channel mask.
00373         """
00374         return self.mask