source: trunk/python/interactivemask.py @ 2108

Last change on this file since 2108 was 2108, checked in by Kana Sugimoto, 13 years ago

New Development: No

JIRA Issue: No (a bug fix)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: interactive test is necessary

Put in Release Notes: No

Module(s): sdbaseline, sdstat, sdfit

Description:

disabled page change button during interactive mask selection.
messages are printed to the logger.


File size: 10.5 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(),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(),False)
95                else:
96                        self.mask=_n_bools(self.scan.nchan(),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 or self.p._plotter.is_dead:
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
166                # Plot selected spectra if needed
167                if self.scan != self.p._data:
168                        if len(self.scan.getifnos()) > 16:
169                                asaplog.post()
170                                asaplog.push("Number of panels > 16. Plotting the first 16...")
171                                asaplog.post("WARN")
172                        # Need replot
173                        self.p._plotter.legend(1)
174                        self.p.plot(self.scan)
175                        # disable casa toolbar
176                        if self.p._plotter.figmgr.casabar:
177                                self.p._plotter.figmgr.casabar.disable_button()
178                                self.p._plotter.figmgr.casabar.disable_prev()
179                                self.p._plotter.figmgr.casabar.disable_next()
180                        for panel in self.p._plotter.subplots:
181                                xmin, xmax = panel['axes'].get_xlim()
182                                marg = 0.05*abs(xmax-xmin)
183                                panel['axes'].set_xlim(xmin-marg, xmax+marg)
184                                if rcParams['plotter.ganged']: break
185                        self.p._plotter.show()
186
187                # Plot initial mask region
188                #if self.showmask or not self.once:
189                if self.showmask:
190                        self._plot_mask()
191                        print ''
192                        print 'Selected regions are shaded with yellow. (gray: projections)'
193                        print 'Now you can modify the selection.'
194                        print 'Draw rectangles with Left-mouse to add the regions,'
195                        print 'or with Right-mouse to exclude the regions.'
196
197
198                if self.event != None:
199                        self._region_start(self.event)
200                else:
201                        self.p._plotter.register('button_press',None)
202                        self.p._plotter.register('button_press',self._region_start)
203
204
205        def _region_start(self,event):
206                # Do not fire event when in zooming/panning mode
207                mode = self.p._plotter.figmgr.toolbar.mode
208                if not mode =='':
209                        return
210                # Return if selected point is out of panel
211                if event.inaxes == None: return
212                # Select mask/unmask region with mask
213                self.rect = {'button': event.button, 'axes': event.inaxes,
214                                 'x': event.x, 'y': event.y,
215                                 'world': [event.xdata, event.ydata,
216                                           event.xdata, event.ydata],
217                                 'pixel': [event.x, event.y,
218                                           event.x, event.y]}
219                self.p._plotter.register('motion_notify', self._region_draw)
220                self.p._plotter.register('button_release', self._region_end)
221
222        def _region_draw(self,event):
223                sameaxes=(event.inaxes == self.rect['axes'])
224                if sameaxes:
225                        xnow=event.x
226                        ynow=event.y
227                        self.xold=xnow
228                        self.yold=ynow
229                        self.xdataold=event.xdata
230                        self.ydataold=event.ydata
231                else:
232                        xnow=self.xold
233                        ynow=self.yold
234
235                self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
236
237        def _region_end(self,event):
238                self.p._plotter.register('motion_notify', None)
239                self.p._plotter.register('button_release', None)
240
241                # Delete the rubber band
242                self.p._plotter.figmgr.toolbar.release(event)
243
244                if event.inaxes == self.rect['axes']:
245                        xend=event.x
246                        yend=event.y
247                        xdataend=event.xdata
248                        ydataend=event.ydata
249                else:
250                        xend=self.xold
251                        yend=self.yold
252                        xdataend=self.xdataold
253                        ydataend=self.ydataold
254
255                self.rect['world'][2:4] = [xdataend, ydataend]
256                self.rect['pixel'][2:4] = [xend, yend]
257                self._update_mask()
258                # Clear up region selection
259                self.rect={}
260                self.xold=None
261                self.yold=None
262                self.xdataold=None
263                self.ydataold=None
264                if self.once: self.finish_selection(callback=self.callback)
265
266        def _update_mask(self):
267                # Min and Max for new mask
268                xstart=self.rect['world'][0]
269                xend=self.rect['world'][2]
270                if xstart <= xend: newlist=[xstart,xend]
271                else: newlist=[xend,xstart]
272                # Mask or unmask
273                invmask=None
274                if self.rect['button'] == 1:
275                        invmask=False
276                        mflg='Mask'
277                elif self.rect['button'] == 3:
278                        invmask=True
279                        mflg='UNmask'
280                asaplog.push(mflg+': '+str(newlist))
281                asaplog.post()
282                newmask=self.scan.create_mask(newlist,invert=invmask)
283                # Logic operation to update mask
284                if invmask:
285                        self.mask=mask_and(self.mask,newmask)
286                else:
287                        self.mask=mask_or(self.mask,newmask)
288                # Plot masked regions
289                #if self.showmask or not self.once: self._plot_mask()
290                if self.showmask: self._plot_mask()
291
292        # Plot masked regions
293        def _plot_mask(self):
294                msks = []
295                msks = self.scan.get_masklist(self.mask,row=0)
296                # Get projection masks for multi-IF
297                ifs=self.scan.getifnos()
298                projs = []
299                if len(ifs) > 1:
300                        row0if=self.scan.getif(0)
301                        for ifno in ifs:
302                                if ifno == row0if: continue
303                                for row in xrange(self.scan.nrow()):
304                                        if self.scan.getif(row) == ifno:
305                                                projs.append(self.scan.get_masklist(self.mask,row=row))
306                                                break
307                if len(self._polygons)>0:
308                        # Remove old polygons
309                        for polygon in self._polygons: polygon.remove()
310                        self._polygons=[]
311                # Plot new polygons
312                if len(msks) > 0:
313                        npanel=len(self.p._plotter.subplots)
314                        j=-1
315                        for iloop in range(len(msks)*npanel):
316                                i = iloop % len(msks)
317                                if  i == 0 : j += 1
318                                if len(ifs) > 1:
319                                        for k in xrange(len(ifs)-1):
320                                                self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))
321                                self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(msks[i][0],msks[i][1],facecolor='yellow'))
322                self.p._plotter.canvas.draw()
323
324        def finish_selection(self, callback=None):
325                """
326                Execute callback function, reset or close plotter window as
327                necessary.
328
329                Parameters:
330                        callback: The post processing function to run after
331                                          the mask selections are completed.
332                                  Specifying the callback function here will overwrite
333                                  the one set by set_callback(func)
334
335                Note this function is automatically called at the end of
336                select_mask() if once=True.
337                """
338                if callback: self.callback=callback
339                if self.callback: self.callback()
340                if not self.event: self.p._plotter.register('button_press',None)
341                # Finish the plot
342                if not self.newplot:
343                        self.clear_polygon()
344                else:
345                        self.p._plotter.unmap()
346                        self.p._plotter = None
347                        del self.p
348                        self.p=None
349                        self._polygons=[]
350
351
352        def clear_polygon(self):
353                """
354                Erase masks plots from the plotter.
355                """
356                if len(self._polygons)>0:
357                        # Remove old polygons
358                        for polygon in self._polygons: polygon.remove()
359                        self.p._plotter.show()
360                        self._polygons=[]
361
362
363        def get_mask(self):
364                """
365                Get the interactively selected channel mask.
366                Returns:
367                        A list of channel mask.
368                """
369                return self.mask
Note: See TracBrowser for help on using the repository browser.