source: branches/alma/python/interactivemask.py @ 1667

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

New Development: No

JIRA Issue: No

Ready to Release: Yes

Interface Changes: No

What Interface Changed:

Test Programs: run sdbaseline with interactive=True

Put in Release Notes: No

Module(s): sdbaseline, sdstat

Description: fixed a bug that caused setting plot range too wide.


File size: 11.3 KB
RevLine 
[1460]1from asap import rcParams
2from asap import _n_bools, mask_and, mask_or
3from asap.scantable import scantable
4
5class interactivemask:
6        """
7        The class for interactive mask selection.
8
9        Example:
[1621]10           my_mask=interactivemask(plotter,scan)
11           my_mask.set_basemask(masklist=[[0,10],[90,100]],invert=False)
12           # Do interactive mask selection
13           my_mask.select_mask()
14           finish=raw_input('Press return to finish selection.\n')
15           my_mask.finish_selection(callback=func)
16           mask=my_mask.get_mask()
[1460]17           
18        Modify mask region by selecting a region on a plot with mouse.
19        """
20
[1621]21        def __init__(self,plotter=None, scan=None):
[1460]22                """
[1621]23                Create a interactive masking object.
24                Either or both 'plotter' or/and 'scan' should be defined.
25
26                Parameters:
27                   plotter: an ASAP plotter object for interactive selection
28                   scan: a scantable to create a mask interactively
[1460]29                """
[1621]30                # Return if GUI is not active
31                if not rcParams['plotter.gui']:
32                        print 'GUI plotter is disabled.\n'
33                        print 'Exit interactive mode.'
34                        return
35                # Verify input parameters
36                if scan is None and plotter is None:
37                        msg = "Either scantable or plotter should be defined."
38                        raise TypeError(msg)
39
[1460]40                self.scan=None
[1621]41                self.p=None
42                self.newplot=False
43                if scan and isinstance(scan, scantable):
44                        self.scan=scan
45                from asap.asapplotter import asapplotter
46                if plotter and isinstance(plotter,asapplotter):
47                        self.p = plotter
48                        if self.scan is None and isinstance(self.p._data,scantable):
49                                self.scan=self.p._data
50                if self.scan is None:
51                        msg = "Invalid scantable."
52                        raise TypeError(msg)
53
54                self.mask=_n_bools(self.scan.nchan(),True)
55                self.callback=None
56                self.event=None
57                self.once=False
58                self.showmask=True
[1460]59                self.rect={}
60                self.xold=None
61                self.yold=None
62                self.xdataold=None
63                self.ydataold=None
64                self._polygons=[]
65       
[1621]66
67        def set_basemask(self,masklist=[],invert=False):
[1460]68                """
[1621]69                Set initial channel mask.
70               
[1460]71                Parameters:
72                    masklist:  [[min, max], [min2, max2], ...]
73                               A list of pairs of start/end points (inclusive)
74                               specifying the regions to be masked
75                    invert:    optional argument. If specified as True,
76                               return an inverted mask, i.e. the regions
77                               specified are excluded
[1621]78                You can reset the mask selection by running this method with
79                the default parameters.
[1460]80                """
81                # Verify input parameters
82                if not (isinstance(masklist, list) or isinstance(masklist, tuple)) \
83                   or not isinstance(invert, bool):
84                        msg = 'Invalid mask definition'
85                        raise TypeError(msg)
86
[1621]87                # Create base mask
[1460]88                if ( len(masklist) > 0 ):
89                        self.mask=self.scan.create_mask(masklist,invert=invert)
[1621]90                elif invert==True:
91                        self.mask=_n_bools(self.scan.nchan(),False)
92                else:
[1460]93                        self.mask=_n_bools(self.scan.nchan(),True)
94
[1621]95
96        def set_startevent(self,event):
97                """
98                Inherit an event from the parent function.
99               
100                Parameters:
101                    event: 'button_press_event' object to be inherited to
102                           start interactive region selection .
103                """
104                from matplotlib.backend_bases import MouseEvent
105                if isinstance(event,MouseEvent) and event.name=='button_press_event':
106                        self.event=event
107                else:
108                        msg="Invalid event."
109                        raise TypeError(msg)   
110
111        def set_callback(self,callback):
112                """
113                Set callback function to run when finish_selection() is executed.
114                    callback: The post processing function to run after
115                              the mask selections are completed.
116                              This will be overwritten if callback is defined in
117                              finish_selection(callback=func)
118                """
119                self.callback=callback
120
121        def select_mask(self,once=False,showmask=True):
122                """
123                Do interactive mask selection.
124                Modify masks interactively by adding/deleting regions with
125                mouse drawing.(left-button: mask; right-button: UNmask)
126                Note that the interactive region selection is available only
127                when GUI plotter is active.
128
129                Parameters:
130                    once:     If specified as True, you can modify masks only
131                              once. Else if False, you can modify them repeatedly.
132                    showmask: If specified as True, the masked regions are plotted
133                              on the plotter.
134                              Note this parameter is valid only when once=True.
135                              Otherwise, maskes are forced to be plotted for reference.
136                """
[1460]137                # Return if GUI is not active
138                if not rcParams['plotter.gui']:
139                        print 'GUI plotter is disabled.\n'
140                        print 'Exit interactive mode.'
141                        return
142
[1621]143                self.once = once
144                if self.once:
145                        self.showmask=showmask
146                else:
147                        if not showmask: print 'Warning: showmask spcification is ignored. Mask regions are plotted anyway.'
148                        self.showmask=True
149
150                #if not self.p._plotter or self.p._plotter.is_dead:
151                if not self.p or self.p._plotter.is_dead:
152                        print 'A new ASAP plotter will be loaded'
[1460]153                        from asap.asapplotter import asapplotter
[1621]154                        self.p=asapplotter()
155                        self.newplot=True
[1460]156
[1621]157                # Plot selected spectra if needed
158                if self.scan != self.p._data:
159                        # Need replot
160                        self.p.plot(self.scan)
161                        for panel in self.p._plotter.subplots:
162                                xmin, xmax = panel['axes'].get_xlim()
[1667]163                                marg = 0.05*abs(xmax-xmin)
[1621]164                                panel['axes'].set_xlim(xmin-marg, xmax+marg)
[1667]165                                if rcParams['plotter.ganged']: break
[1621]166                        self.p._plotter.show()
[1460]167
[1621]168                # Plot initial mask region
169                #if self.showmask or not self.once:
170                if self.showmask:
171                        self._plot_mask()
172                        print ''
173                        print 'Selected regions are shaded with yellow. (gray: projections)'
174                        print 'Now you can modify the selection.'
175                        print 'Draw rectangles with Left-mouse to add the regions,'
176                        print 'or with Right-mouse to exclude the regions.'
[1460]177
[1621]178
179                if self.event != None:
180                        self._region_start(self.event)
181                else:
182                        self.p._plotter.register('button_press',None)
183                        self.p._plotter.register('button_press',self._region_start)
184
185
[1460]186        def _region_start(self,event):
187                # Do not fire event when in zooming/panning mode
[1621]188                mode = self.p._plotter.figmgr.toolbar.mode
[1460]189                if not mode =='':
190                        return
191                # Return if selected point is out of panel
192                if event.inaxes == None: return
193                # Select mask/unmask region with mask
194                self.rect = {'button': event.button, 'axes': event.inaxes,
[1621]195                             'x': event.x, 'y': event.y,
[1460]196                             'world': [event.xdata, event.ydata,
197                                       event.xdata, event.ydata],
[1621]198                             'pixel': [event.x, event.y,
199                                       event.x, event.y]}
[1623]200                ### Start mod: 2009/08/17 kana ###
201                #self._default_motion('stop')
202                ### End mod ######################
[1621]203                self.p._plotter.register('motion_notify', self._region_draw)
204                self.p._plotter.register('button_release', self._region_end)
[1460]205
206        def _region_draw(self,event):
207                sameaxes=(event.inaxes == self.rect['axes'])
208                if sameaxes:
209                        xnow=event.x
210                        ynow=event.y
211                        self.xold=xnow
212                        self.yold=ynow
213                        self.xdataold=event.xdata
214                        self.ydataold=event.ydata
215                else:
216                        xnow=self.xold
217                        ynow=self.yold
218
[1621]219                self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
[1623]220                ### Start mod: 2009/08/17 kana ###
221                #self.p._plotter.figmgr.toolbar.mouse_move(event)
222                ### End mod ######################
[1621]223
[1460]224        def _region_end(self,event):
[1621]225                self.p._plotter.register('motion_notify', None)
[1623]226                ### Start mod: 2009/08/17 kana ###
227                #self._default_motion('start')
228                ### End mod ######################
[1621]229                self.p._plotter.register('button_release', None)
230
231                self.p._plotter.figmgr.toolbar.release(event)
[1460]232               
233                if event.inaxes == self.rect['axes']:
234                        xend=event.x
235                        yend=event.y
236                        xdataend=event.xdata
237                        ydataend=event.ydata
238                else:
239                        xend=self.xold
240                        yend=self.yold
241                        xdataend=self.xdataold
242                        ydataend=self.ydataold
243                       
244                self.rect['world'][2:4] = [xdataend, ydataend]
[1621]245                self.rect['pixel'][2:4] = [xend, yend]
[1460]246                self._update_mask()
[1623]247                # Clear up region selection
248                self.rect={}
249                self.xold=None
250                self.yold=None
251                self.xdataold=None
252                self.ydataold=None
[1621]253                if self.once: self.finish_selection(callback=self.callback)
[1460]254
255        def _update_mask(self):
256                # Min and Max for new mask
257                xstart=self.rect['world'][0]
258                xend=self.rect['world'][2]
259                if xstart <= xend: newlist=[xstart,xend]
260                else: newlist=[xend,xstart]
261                # Mask or unmask
262                invmask=None
263                if self.rect['button'] == 1:
264                        invmask=False
265                        mflg='Mask'
266                elif self.rect['button'] == 3:
267                        invmask=True
268                        mflg='UNmask'
269                print mflg+': ',newlist
270                newmask=self.scan.create_mask(newlist,invert=invmask)
271                # Logic operation to update mask
272                if invmask:
273                        self.mask=mask_and(self.mask,newmask)
274                else:
275                        self.mask=mask_or(self.mask,newmask)
276                # Plot masked regions
[1621]277                #if self.showmask or not self.once: self._plot_mask()
278                if self.showmask: self._plot_mask()
[1460]279
280        # Plot masked regions
281        def _plot_mask(self):
282                msks = []
283                msks = self.scan.get_masklist(self.mask,row=0)
284                # Get projection masks for multi-IF
285                ifs=self.scan.getifnos()
286                projs = []
287                if len(ifs) > 1:
288                        row0if=self.scan.getif(0)
289                        for ifno in ifs:
290                                if ifno == row0if: continue
291                                for row in xrange(self.scan.nrow()):
292                                        if self.scan.getif(row) == ifno:
293                                                projs.append(self.scan.get_masklist(self.mask,row=row))
294                                                break
295                if len(self._polygons)>0:
296                        # Remove old polygons
297                        for polygon in self._polygons: polygon.remove()
298                        self._polygons=[]
299                # Plot new polygons
[1498]300                if len(msks) > 0:
[1621]301                        npanel=len(self.p._plotter.subplots)
[1498]302                        j=-1
303                        for iloop in range(len(msks)*npanel):
304                                i = iloop % len(msks)
305                                if  i == 0 : j += 1
306                                if len(ifs) > 1:
307                                        for k in xrange(len(ifs)-1):
[1621]308                                                self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))                   
309                                self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(msks[i][0],msks[i][1],facecolor='yellow'))
310                self.p._plotter.canvas.draw()
[1460]311
[1623]312        ### Start mod: 2009/08/17 kana ###
313        def _default_motion(self,action):
314                if not isinstance(action,str):
315                        print "WARN: Either 'stop' or 'start' is valid."
316                        return
317               
318                canvas=self.p._plotter.canvas
319                toolbar=self.p._plotter.figmgr.toolbar
320                if action == 'stop':
321                        if toolbar._idDrag:
322                                print "WARN: No default event. Nothing to be done."
323                                return
324                        canvas.mpl_disconnect(toolbar._idDrag)
325                        print "Disconnecting default motion event ", toolbar._idDrag
326                elif action == 'start':
327                        if toolbar._idDrag:
328                                print "WARN: Default event already exists. Disconnecting the event."
329                                canvas.mpl_disconnect(toolbar._idDrag)
330                        toolbar._idDrag=canvas.mpl_connect('motion_notify_event',toolbar.mouse_move)
331                        print "Connecting default motion event ", toolbar._idDrag                       
332                else:
333                        print "WARN: Either 'stop' or 'start' is valid."
334
335        ### End mod ######################
336               
[1621]337        def finish_selection(self, callback=None):
[1623]338                """
339                Execute callback function, reset or close plotter window as
340                necessary.
341
342                Parameters:
343                    callback: The post processing function to run after
344                              the mask selections are completed.
345                              Specifying the callback function here will overwrite
346                              the one set by set_callback(func)
347               
348                Note this function is automatically called at the end of
349                select_mask() if once=True.
350                """
[1621]351                if callback: self.callback=callback
352                if self.callback: self.callback()
[1622]353                if not self.event: self.p._plotter.register('button_press',None)
[1621]354                # Finish the plot
355                if not self.newplot:
356                        self.clear_polygon()
357                else:
358                        self.p._plotter.unmap()
359                        self.p._plotter = None
360                        del self.p
361                        self.p=None
[1623]362                        self._polygons=[]
[1621]363
364
365        def clear_polygon(self):
366                """
367                Erase masks plots from the plotter.
368                """
369                if len(self._polygons)>0:
370                        # Remove old polygons
371                        for polygon in self._polygons: polygon.remove()
372                        self.p._plotter.show()
373                        self._polygons=[]
374
375
[1460]376        def get_mask(self):
377                """
378                Get the interactively selected channel mask.
379                Returns:
380                    A list of channel mask.
381                """
382                return self.mask
383
[1621]384
Note: See TracBrowser for help on using the repository browser.