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

Last change on this file since 1696 was 1667, checked in by Kana Sugimoto, 15 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.