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

Last change on this file since 1705 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
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.