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
Line 
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:
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()
17           
18        Modify mask region by selecting a region on a plot with mouse.
19        """
20
21        def __init__(self,plotter=None, scan=None):
22                """
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
29                """
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
40                self.scan=None
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
59                self.rect={}
60                self.xold=None
61                self.yold=None
62                self.xdataold=None
63                self.ydataold=None
64                self._polygons=[]
65       
66
67        def set_basemask(self,masklist=[],invert=False):
68                """
69                Set initial channel mask.
70               
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
78                You can reset the mask selection by running this method with
79                the default parameters.
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
87                # Create base mask
88                if ( len(masklist) > 0 ):
89                        self.mask=self.scan.create_mask(masklist,invert=invert)
90                elif invert==True:
91                        self.mask=_n_bools(self.scan.nchan(),False)
92                else:
93                        self.mask=_n_bools(self.scan.nchan(),True)
94
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                """
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
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'
153                        from asap.asapplotter import asapplotter
154                        self.p=asapplotter()
155                        self.newplot=True
156
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()
163                                marg = 0.05*abs(xmax-xmin)
164                                panel['axes'].set_xlim(xmin-marg, xmax+marg)
165                                if rcParams['plotter.ganged']: break
166                        self.p._plotter.show()
167
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.'
177
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
186        def _region_start(self,event):
187                # Do not fire event when in zooming/panning mode
188                mode = self.p._plotter.figmgr.toolbar.mode
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,
195                             'x': event.x, 'y': event.y,
196                             'world': [event.xdata, event.ydata,
197                                       event.xdata, event.ydata],
198                             'pixel': [event.x, event.y,
199                                       event.x, event.y]}
200                ### Start mod: 2009/08/17 kana ###
201                #self._default_motion('stop')
202                ### End mod ######################
203                self.p._plotter.register('motion_notify', self._region_draw)
204                self.p._plotter.register('button_release', self._region_end)
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
219                self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
220                ### Start mod: 2009/08/17 kana ###
221                #self.p._plotter.figmgr.toolbar.mouse_move(event)
222                ### End mod ######################
223
224        def _region_end(self,event):
225                self.p._plotter.register('motion_notify', None)
226                ### Start mod: 2009/08/17 kana ###
227                #self._default_motion('start')
228                ### End mod ######################
229                self.p._plotter.register('button_release', None)
230
231                self.p._plotter.figmgr.toolbar.release(event)
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]
245                self.rect['pixel'][2:4] = [xend, yend]
246                self._update_mask()
247                # Clear up region selection
248                self.rect={}
249                self.xold=None
250                self.yold=None
251                self.xdataold=None
252                self.ydataold=None
253                if self.once: self.finish_selection(callback=self.callback)
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
277                #if self.showmask or not self.once: self._plot_mask()
278                if self.showmask: self._plot_mask()
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
300                if len(msks) > 0:
301                        npanel=len(self.p._plotter.subplots)
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):
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()
311
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               
337        def finish_selection(self, callback=None):
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                """
351                if callback: self.callback=callback
352                if self.callback: self.callback()
353                if not self.event: self.p._plotter.register('button_press',None)
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
362                        self._polygons=[]
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
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
384
Note: See TracBrowser for help on using the repository browser.