source: branches/casa-prerelease/pre-asap/python/interactivemask.py@ 2153

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

merged bug fixes in trunk (r2108)

File size: 10.5 KB
RevLine 
[1826]1from asap.parameters import rcParams
2from asap.utils import _n_bools, mask_and, mask_or
[1460]3from asap.scantable import scantable
[2110]4from asap.logging import asaplog, asaplog_post_dec
[1460]5
6class interactivemask:
7 """
8 The class for interactive mask selection.
9
10 Example:
[1621]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()
[1826]18
19 Modify mask region by selecting a region on a plot with mouse.
[1460]20 """
21
[1621]22 def __init__(self,plotter=None, scan=None):
[1460]23 """
[1621]24 Create a interactive masking object.
[1826]25 Either or both 'plotter' or/and 'scan' should be defined.
[1621]26
27 Parameters:
28 plotter: an ASAP plotter object for interactive selection
29 scan: a scantable to create a mask interactively
[1460]30 """
[1621]31 # Return if GUI is not active
32 if not rcParams['plotter.gui']:
[2110]33 msg = 'GUI plotter is disabled.\n'
34 msg += 'Exit interactive mode.'
35 asaplog.push(msg)
36 asaplog.post("ERROR")
[1621]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
[1460]43 self.scan=None
[1621]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
[1460]62 self.rect={}
63 self.xold=None
64 self.yold=None
65 self.xdataold=None
66 self.ydataold=None
67 self._polygons=[]
[1621]68
[1826]69
[1621]70 def set_basemask(self,masklist=[],invert=False):
[1460]71 """
[1621]72 Set initial channel mask.
[1826]73
[1460]74 Parameters:
[2110]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
[1621]81 You can reset the mask selection by running this method with
[1826]82 the default parameters.
[1460]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
[1621]90 # Create base mask
[1460]91 if ( len(masklist) > 0 ):
92 self.mask=self.scan.create_mask(masklist,invert=invert)
[2110]93 elif invert==True:
[1621]94 self.mask=_n_bools(self.scan.nchan(),False)
95 else:
[1460]96 self.mask=_n_bools(self.scan.nchan(),True)
97
[1621]98
99 def set_startevent(self,event):
100 """
101 Inherit an event from the parent function.
[1826]102
[1621]103 Parameters:
[2110]104 event: 'button_press_event' object to be inherited to
105 start interactive region selection .
[1621]106 """
107 from matplotlib.backend_bases import MouseEvent
[1826]108 if isinstance(event,MouseEvent) and event.name=='button_press_event':
[1621]109 self.event=event
110 else:
111 msg="Invalid event."
[1826]112 raise TypeError(msg)
[1621]113
114 def set_callback(self,callback):
115 """
[1826]116 Set callback function to run when finish_selection() is executed.
[2110]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)
[1621]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
[1826]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.
[1621]131
132 Parameters:
[2110]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.
[1621]139 """
[1460]140 # Return if GUI is not active
141 if not rcParams['plotter.gui']:
[2110]142 msg = 'GUI plotter is disabled.\n'
143 msg += 'Exit interactive mode.'
144 asaplog.push(msg)
145 asaplog.post("ERROR")
[1460]146 return
147
[1621]148 self.once = once
149 if self.once:
150 self.showmask=showmask
151 else:
[2110]152 if not showmask:
153 asaplog.post()
154 asaplog.push('showmask spcification is ignored. Mask regions are plotted anyway.')
155 asaplog.post("WARN")
[1621]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:
[2110]160 asaplog.push('A new ASAP plotter will be loaded')
161 asaplog.post()
[1460]162 from asap.asapplotter import asapplotter
[1621]163 self.p=asapplotter()
164 self.newplot=True
[1460]165
[2110]166 # Plot selected spectra if needed
[1621]167 if self.scan != self.p._data:
[2110]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")
[1621]172 # Need replot
[2110]173 self.p._plotter.legend(1)
[1621]174 self.p.plot(self.scan)
[1724]175 # disable casa toolbar
[2110]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()
[1621]180 for panel in self.p._plotter.subplots:
181 xmin, xmax = panel['axes'].get_xlim()
[1667]182 marg = 0.05*abs(xmax-xmin)
[1621]183 panel['axes'].set_xlim(xmin-marg, xmax+marg)
[1667]184 if rcParams['plotter.ganged']: break
[1621]185 self.p._plotter.show()
[1460]186
[1621]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.'
[1460]196
[1621]197
198 if self.event != None:
199 self._region_start(self.event)
[1826]200 else:
[1621]201 self.p._plotter.register('button_press',None)
202 self.p._plotter.register('button_press',self._region_start)
203
204
[1460]205 def _region_start(self,event):
206 # Do not fire event when in zooming/panning mode
[1621]207 mode = self.p._plotter.figmgr.toolbar.mode
[1460]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,
[2110]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]}
[1621]219 self.p._plotter.register('motion_notify', self._region_draw)
220 self.p._plotter.register('button_release', self._region_end)
[1460]221
222 def _region_draw(self,event):
223 sameaxes=(event.inaxes == self.rect['axes'])
[1826]224 if sameaxes:
[1460]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
[1621]235 self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
236
[1460]237 def _region_end(self,event):
[1621]238 self.p._plotter.register('motion_notify', None)
239 self.p._plotter.register('button_release', None)
240
[1767]241 # Delete the rubber band
[1621]242 self.p._plotter.figmgr.toolbar.release(event)
[1826]243
244 if event.inaxes == self.rect['axes']:
[1460]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
[1826]254
[1460]255 self.rect['world'][2:4] = [xdataend, ydataend]
[1621]256 self.rect['pixel'][2:4] = [xend, yend]
[1460]257 self._update_mask()
[1623]258 # Clear up region selection
259 self.rect={}
260 self.xold=None
261 self.yold=None
262 self.xdataold=None
263 self.ydataold=None
[1621]264 if self.once: self.finish_selection(callback=self.callback)
[1460]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'
[2110]280 asaplog.push(mflg+': '+str(newlist))
281 asaplog.post()
[1460]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
[1621]289 #if self.showmask or not self.once: self._plot_mask()
290 if self.showmask: self._plot_mask()
[1460]291
292 # Plot masked regions
293 def _plot_mask(self):
[2110]294 msks = []
295 msks = self.scan.get_masklist(self.mask,row=0)
[1460]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
[2110]307 if len(self._polygons)>0:
[1460]308 # Remove old polygons
309 for polygon in self._polygons: polygon.remove()
310 self._polygons=[]
311 # Plot new polygons
[1498]312 if len(msks) > 0:
[1621]313 npanel=len(self.p._plotter.subplots)
[1498]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):
[1826]320 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))
[1621]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()
[1460]323
[1621]324 def finish_selection(self, callback=None):
[1623]325 """
326 Execute callback function, reset or close plotter window as
327 necessary.
328
329 Parameters:
[2110]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)
[1826]334
[1623]335 Note this function is automatically called at the end of
[1826]336 select_mask() if once=True.
[1623]337 """
[1621]338 if callback: self.callback=callback
339 if self.callback: self.callback()
[1622]340 if not self.event: self.p._plotter.register('button_press',None)
[1621]341 # Finish the plot
342 if not self.newplot:
343 self.clear_polygon()
[1826]344 else:
[1621]345 self.p._plotter.unmap()
346 self.p._plotter = None
347 del self.p
348 self.p=None
[1623]349 self._polygons=[]
[1621]350
351
352 def clear_polygon(self):
353 """
[1826]354 Erase masks plots from the plotter.
[1621]355 """
[2110]356 if len(self._polygons)>0:
[1621]357 # Remove old polygons
358 for polygon in self._polygons: polygon.remove()
359 self.p._plotter.show()
360 self._polygons=[]
361
362
[1460]363 def get_mask(self):
364 """
365 Get the interactively selected channel mask.
366 Returns:
[2110]367 A list of channel mask.
[1460]368 """
369 return self.mask
Note: See TracBrowser for help on using the repository browser.