source: trunk/python/interactivemask.py@ 1924

Last change on this file since 1924 was 1826, checked in by Malte Marquarding, 14 years ago

Tidy up of imports (now imported from asap.). Also fixed some whitespace/tab issues

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