00001 from asap.parameters import rcParams
00002 from asap.utils import _n_bools, mask_and, mask_or
00003 from asap.scantable import scantable
00004 from asap.logging import asaplog, asaplog_post_dec
00005
00006 class interactivemask:
00007 """
00008 The class for interactive mask selection.
00009
00010 Example:
00011 my_mask=interactivemask(plotter,scan)
00012 my_mask.set_basemask(masklist=[[0,10],[90,100]],invert=False)
00013 # Do interactive mask selection
00014 my_mask.select_mask()
00015 finish=raw_input('Press return to finish selection.\n')
00016 my_mask.finish_selection(callback=func)
00017 mask=my_mask.get_mask()
00018
00019 Modify mask region by selecting a region on a plot with mouse.
00020 """
00021
00022 def __init__(self,plotter=None, scan=None):
00023 """
00024 Create a interactive masking object.
00025 Either or both 'plotter' or/and 'scan' should be defined.
00026
00027 Parameters:
00028 plotter: an ASAP plotter object for interactive selection
00029 scan: a scantable to create a mask interactively
00030 """
00031
00032 if not rcParams['plotter.gui']:
00033 msg = 'GUI plotter is disabled.\n'
00034 msg += 'Exit interactive mode.'
00035 asaplog.push(msg)
00036 asaplog.post("ERROR")
00037 return
00038
00039 if scan is None and plotter is None:
00040 msg = "Either scantable or plotter should be defined."
00041 raise TypeError(msg)
00042
00043 self.scan = None
00044 self.p = None
00045 self.newplot = False
00046 if scan and isinstance(scan, scantable):
00047 self.scan = scan
00048 from asap.asapplotter import asapplotter
00049 if plotter and isinstance(plotter,asapplotter):
00050 self.p = plotter
00051 if self.scan is None and isinstance(self.p._data,scantable):
00052 self.scan = self.p._data
00053 if self.scan is None:
00054 msg = "Invalid scantable."
00055 raise TypeError(msg)
00056
00057 self.mask = _n_bools(self.scan.nchan(self.scan.getif(0)),True)
00058 self.callback = None
00059 self.event = None
00060 self.once = False
00061 self.showmask = True
00062 self.rect = {}
00063 self.xold = None
00064 self.yold = None
00065 self.xdataold = None
00066 self.ydataold = None
00067 self._polygons = []
00068
00069
00070 def set_basemask(self,masklist=[],invert=False):
00071 """
00072 Set initial channel mask.
00073
00074 Parameters:
00075 masklist: [[min, max], [min2, max2], ...]
00076 A list of pairs of start/end points (inclusive)
00077 specifying the regions to be masked
00078 invert: optional argument. If specified as True,
00079 return an inverted mask, i.e. the regions
00080 specified are excluded
00081 You can reset the mask selection by running this method with
00082 the default parameters.
00083 """
00084
00085 if not (isinstance(masklist, list) or isinstance(masklist, tuple)) \
00086 or not isinstance(invert, bool):
00087 msg = 'Invalid mask definition'
00088 raise TypeError(msg)
00089
00090
00091 if ( len(masklist) > 0 ):
00092 self.mask = self.scan.create_mask(masklist,invert=invert)
00093 elif invert == True:
00094 self.mask = _n_bools(self.scan.nchan(self.scan.getif(0)),False)
00095 else:
00096 self.mask = _n_bools(self.scan.nchan(self.scan.getif(0)),True)
00097
00098
00099 def set_startevent(self,event):
00100 """
00101 Inherit an event from the parent function.
00102
00103 Parameters:
00104 event: 'button_press_event' object to be inherited to
00105 start interactive region selection .
00106 """
00107 from matplotlib.backend_bases import MouseEvent
00108 if isinstance(event,MouseEvent) and event.name == 'button_press_event':
00109 self.event = event
00110 else:
00111 msg = "Invalid event."
00112 raise TypeError(msg)
00113
00114 def set_callback(self,callback):
00115 """
00116 Set callback function to run when finish_selection() is executed.
00117 callback: The post processing function to run after
00118 the mask selections are completed.
00119 This will be overwritten if callback is defined in
00120 finish_selection(callback=func)
00121 """
00122 self.callback = callback
00123
00124 def select_mask(self,once=False,showmask=True):
00125 """
00126 Do interactive mask selection.
00127 Modify masks interactively by adding/deleting regions with
00128 mouse drawing.(left-button: mask; right-button: UNmask)
00129 Note that the interactive region selection is available only
00130 when GUI plotter is active.
00131
00132 Parameters:
00133 once: If specified as True, you can modify masks only
00134 once. Else if False, you can modify them repeatedly.
00135 showmask: If specified as True, the masked regions are plotted
00136 on the plotter.
00137 Note this parameter is valid only when once=True.
00138 Otherwise, maskes are forced to be plotted for reference.
00139 """
00140
00141 if not rcParams['plotter.gui']:
00142 msg = 'GUI plotter is disabled.\n'
00143 msg += 'Exit interactive mode.'
00144 asaplog.push(msg)
00145 asaplog.post("ERROR")
00146 return
00147
00148 self.once = once
00149 if self.once:
00150 self.showmask = showmask
00151 else:
00152 if not showmask:
00153 asaplog.post()
00154 asaplog.push('showmask spcification is ignored. Mask regions are plotted anyway.')
00155 asaplog.post("WARN")
00156 self.showmask = True
00157
00158 if not self.p:
00159 asaplog.push('A new ASAP plotter will be loaded')
00160 asaplog.post()
00161 from asap.asapplotter import asapplotter
00162 self.p = asapplotter()
00163 self.newplot = True
00164 self.p._assert_plotter(action='reload')
00165 from matplotlib import rc as rcp
00166 rcp('lines', linewidth=1)
00167
00168
00169 if self.scan != self.p._data:
00170 if len(self.scan.getifnos()) > 16:
00171 asaplog.post()
00172 asaplog.push("Number of panels > 16. Plotting the first 16...")
00173 asaplog.post("WARN")
00174
00175 self.p._legendloc = 1
00176 self.p.plot(self.scan)
00177
00178 if self.p._plotter.figmgr.casabar:
00179 self.p._plotter.figmgr.casabar.disable_button()
00180 self.p._plotter.figmgr.casabar.disable_prev()
00181 self.p._plotter.figmgr.casabar.disable_next()
00182 for panel in self.p._plotter.subplots:
00183 xmin, xmax = panel['axes'].get_xlim()
00184 marg = 0.05*abs(xmax-xmin)
00185 panel['axes'].set_xlim(xmin-marg, xmax+marg)
00186 if rcParams['plotter.ganged']: break
00187 self.p._plotter.show()
00188
00189
00190
00191 if self.showmask:
00192 self._plot_mask()
00193 print ''
00194 print 'Selected regions are shaded with yellow. (gray: projections)'
00195 print 'Now you can modify the selection.'
00196 print 'Draw rectangles with Left-mouse to add the regions,'
00197 print 'or with Right-mouse to exclude the regions.'
00198
00199
00200 if self.event != None:
00201 self._region_start(self.event)
00202 else:
00203 self.p._plotter.register('button_press',None)
00204 self.p._plotter.register('button_press',self._region_start)
00205
00206
00207 def _region_start(self,event):
00208
00209 mode = self.p._plotter.figmgr.toolbar.mode
00210 if not mode == '':
00211 return
00212
00213 if event.inaxes == None: return
00214
00215 self.rect = {'button': event.button, 'axes': event.inaxes,
00216 'x': event.x, 'y': event.y,
00217 'world': [event.xdata, event.ydata,
00218 event.xdata, event.ydata],
00219 'pixel': [event.x, event.y,
00220 event.x, event.y]}
00221 self.p._plotter.register('motion_notify', self._region_draw)
00222 self.p._plotter.register('button_release', self._region_end)
00223
00224 def _region_draw(self,event):
00225 sameaxes=(event.inaxes == self.rect['axes'])
00226 if sameaxes:
00227 xnow = event.x
00228 ynow = event.y
00229 self.xold = xnow
00230 self.yold = ynow
00231 self.xdataold = event.xdata
00232 self.ydataold = event.ydata
00233 else:
00234 xnow = self.xold
00235 ynow = self.yold
00236
00237 self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
00238
00239 def _region_end(self,event):
00240 self.p._plotter.register('motion_notify', None)
00241 self.p._plotter.register('button_release', None)
00242
00243
00244 self.p._plotter.figmgr.toolbar.release(event)
00245
00246 if event.inaxes == self.rect['axes']:
00247 xend = event.x
00248 yend = event.y
00249 xdataend = event.xdata
00250 ydataend = event.ydata
00251 else:
00252 xend = self.xold
00253 yend = self.yold
00254 xdataend = self.xdataold
00255 ydataend = self.ydataold
00256
00257 self.rect['world'][2:4] = [xdataend, ydataend]
00258 self.rect['pixel'][2:4] = [xend, yend]
00259 self._update_mask()
00260
00261 self.rect = {}
00262 self.xold = None
00263 self.yold = None
00264 self.xdataold = None
00265 self.ydataold = None
00266 if self.once: self.finish_selection(callback=self.callback)
00267
00268 def _update_mask(self):
00269
00270 xstart = self.rect['world'][0]
00271 xend = self.rect['world'][2]
00272 if xstart <= xend: newlist=[xstart,xend]
00273 else: newlist = [xend,xstart]
00274
00275 invmask = None
00276 if self.rect['button'] == 1:
00277 invmask = False
00278 mflg = 'Mask'
00279 elif self.rect['button'] == 3:
00280 invmask = True
00281 mflg = 'UNmask'
00282 asaplog.push(mflg+': '+str(newlist))
00283 asaplog.post()
00284 newmask = self.scan.create_mask(newlist,invert=invmask)
00285
00286 if invmask:
00287 self.mask = mask_and(self.mask,newmask)
00288 else:
00289 self.mask = mask_or(self.mask,newmask)
00290
00291
00292 if self.showmask: self._plot_mask()
00293
00294
00295 def _plot_mask(self):
00296 msks = []
00297 msks = self.scan.get_masklist(self.mask,row=0)
00298
00299 ifs = self.scan.getifnos()
00300 projs = []
00301 if len(ifs) > 1:
00302 row0if = self.scan.getif(0)
00303 for ifno in ifs:
00304 if ifno == row0if: continue
00305 for row in xrange(self.scan.nrow()):
00306 if self.scan.getif(row) == ifno:
00307 projs.append(self.scan.get_masklist(self.mask,row=row))
00308 break
00309 if len(self._polygons)>0:
00310
00311 for polygon in self._polygons: polygon.remove()
00312 self._polygons = []
00313
00314 if len(msks) > 0:
00315 npanel = len(self.p._plotter.subplots)
00316 j = -1
00317 for iloop in range(len(msks)*npanel):
00318 i = iloop % len(msks)
00319 if i == 0 : j += 1
00320 if len(ifs) > 1:
00321 for k in xrange(len(ifs)-1):
00322 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))
00323 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(msks[i][0],msks[i][1],facecolor='yellow'))
00324 self.p._plotter.canvas.draw()
00325
00326 def finish_selection(self, callback=None):
00327 """
00328 Execute callback function, reset or close plotter window as
00329 necessary.
00330
00331 Parameters:
00332 callback: The post processing function to run after
00333 the mask selections are completed.
00334 Specifying the callback function here will overwrite
00335 the one set by set_callback(func)
00336
00337 Note this function is automatically called at the end of
00338 select_mask() if once=True.
00339 """
00340 if callback: self.callback=callback
00341 if self.callback: self.callback()
00342 if not self.event:
00343 try: self.p._plotter.register('button_press',None)
00344 except: pass
00345
00346 if not self.newplot:
00347 self.clear_polygon()
00348 else:
00349
00350 self.p._plotter.quit()
00351 self.p._plotter = None
00352 del self.p
00353 self.p = None
00354 self._polygons = []
00355
00356
00357 def clear_polygon(self):
00358 """
00359 Erase masks plots from the plotter.
00360 """
00361 if len(self._polygons) > 0:
00362
00363 for polygon in self._polygons: polygon.remove()
00364 self.p._plotter.show()
00365 self._polygons = []
00366
00367
00368 def get_mask(self):
00369 """
00370 Get the interactively selected channel mask.
00371 Returns:
00372 A list of channel mask.
00373 """
00374 return self.mask