source: trunk/python/interactivemask.py @ 2453

Last change on this file since 2453 was 2453, checked in by Kana Sugimoto, 12 years ago

New Development: No

JIRA Issue: Yes (CAS-3749/Trac-266)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: renamed a parameter of asapplotter._assert_plotter from mode to action

Test Programs:

Put in Release Notes: No

Module(s):

Description: renamed a parameter of asapplotter._assert_plotter from mode to action


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