;+ ; NAME: ; markdata ; PURPOSE: ; Widget for marking/unmarking bad data. ; DESCRIPTION: ; Mouse click and drag will define a selection of points. Newly selected ; points will be marked in green. To mark these as bad, click the 'Bad' ; button. To mark these green points as good, click the 'Good' button. ; Bad points are plotted in red. The rest of the control buttons should ; be self-explanatory. ; CATEGORY: ; Widgets ; CALLING SEQUENCE: ; markdata,y,bad ; markdata,x,y,bad ; markdata,x,y,err,bad ; INPUTS: ; y - Dependent variable. ; bad - Flag array, 0 --> good data, 1 --> bad data. ; OPTIONAL INPUT PARAMETERS: ; x - (3 or 4 arg input) Independent variable (if not supplied, ordinal ; point number is used instead. ; err - (4 arg input) Uncertainty on y. ; KEYWORD INPUT PARAMETERS: ; GROUP: The widget ID of the widget that calls MARKDATA. When this ; ID is specified, a death of the caller results in the death of ; the MARKDATA widget application. ; ; TITLE: A scalar string to be used for the window title. If it is ; not specified, the default title is "Data Editor" ; ; XTITLE: X title for plot, default='x' ; ; YTITLE: Y title for plot, default='y' ; ; PTITLE: Title string for plot. ; ; XSIZE: ysize of plot window, default=600 ; ; YSIZE: ysize of plot window, default=400 ; ; SCALING: 0 -> start with Scale Good (default) ; 1 -> start with Scale Good (default) ; ; XFLIP: Flag, true means to flip the x-axis ; YFLIP: Flag, true means to flip the y-axis ; CONNECT: flag, true means to connect data points with lines ; ; OUTPUTS: ; bad - modified flag array. ; KEYWORD OUTPUT PARAMETERS: ; COMMON BLOCKS: ; MWB_MARKDATA: COMMON block that holds the new vector for the bad values ; during the time it takes to exit the routine. ; SIDE EFFECTS: ; This function initiates the XMANAGER if it is not already running. ; RESTRICTIONS: ; PROCEDURE: ; MODIFICATION HISTORY: ; 96/10/29 - Written by Marc W. Buie, Lowell Observatory ; 97/2/5, MWB, added Clear, and All Bad buttons. ;- ; ------------------------------------------------------------------------------ ; procedure markdata_cleanup ; ------------------------------------------------------------------------------ ; This is used to pass the new bad values array from the structure in the ; widget to the main routine so that the modified values can be returned ; to the calling program. ; ------------------------------------------------------------------------------ pro markdata_cleanup,id common mwb_markdata,badvals widget_control,id,GET_UVALUE=badvals end ; ------------------------------------------------------------------------------ ; procedure markdata_plot ; ------------------------------------------------------------------------------ ; Plot the data, marking the three data states ; ------------------------------------------------------------------------------ pro markdata_plot, state ; Find data not marked bad z=where(state.bad eq 0,count) if count eq 0 then $ message,'Warning no good data left!',/info ; Determine the plot scaling. if state.scaling eq 0 or count eq 0 then begin xr=minmax(state.x,/nan) yr=minmax(state.y,/nan) endif else begin xr=minmax(state.x[z],/nan) yr=minmax(state.y[z],/nan) endelse if state.xflip then xr=reverse(xr) if state.yflip then yr=reverse(yr) ; Plot axes plot,[0],[1],xr=xr,yr=yr,/nodata, $ xtitle=state.xtitle,ytitle=state.ytitle,title=state.ptitle ; Plot good and new data if count ne 0 and state.errs then $ oploterror,[state.x[z]],[state.y[z]],[state.err[z]], $ psym=state.connect*8,color=state.color[9],symsize=0.75+state.connect*0.25 if count ne 0 and not state.errs then $ oplot,[state.x[z]],[state.y[z]],psym=state.connect*8,color=state.color[9], $ symsize=0.75+state.connect*0.25 ; Plot bad data z=where(state.bad eq 1,count) if count ne 0 then oplot,[state.x[z]],[state.y[z]],psym=8,color=state.color[1], $ symsize=0.75+state.connect*0.25 ; Highlight newly marked data z=where(state.new eq 1,count) if count ne 0 then oplot,[state.x[z]],[state.y[z]],psym=8,color=state.color[2], $ symsize=0.75+state.connect*0.25 end ; ------------------------------------------------------------------------------ ; procedure markdata_ev ; ------------------------------------------------------------------------------ ; This procedure processes the events being sent by the XManager. ; ------------------------------------------------------------------------------ pro markdata_ev, event ; Get the state stash=widget_info(event.handler,/CHILD) widget_control,stash,GET_UVALUE=state,/NO_COPY plotit=1 case event.id of state.doneid: begin widget_control,event.handler,SET_UVALUE=state.bad widget_control,stash,SET_UVALUE=state,/NO_COPY widget_control,event.top,/DESTROY return end state.allbadid: begin state.bad[*]=1 end state.allid: begin state.bad[*]=0 ; Protection against NaN's z=where(finite(state.y) eq 0,count) if count ne 0 then begin print,'MARKDATA: warning, ',strcompress(count), $ ' NaN values automatically marked bad.' state.bad[z]=1 endif end state.badid: begin z=where(state.new eq 1,count) if count ne 0 then begin state.bad[z]=1 state.new[z]=0 endif end state.clearid: begin state.new[*]=0 end state.connectid: begin if state.connect eq 1 then state.connect = -1 else state.connect = 1 end state.drawid: begin case event.type of 0: begin ; button press state.x1 = event.x state.y1 = event.y state.lastx = event.x state.lasty = event.y state.drag = 1 device,set_graphics_function=6 end 1: begin ; button release state.x2 = event.x state.y2 = event.y state.drag = 0 device,set_graphics_function=3 xlim=minmax([state.x1,state.x2]) ylim=minmax([state.y1,state.y2]) lim=convert_coord(xlim,ylim,/device,/to_data) z=where(state.x ge min(lim[0,*]) and $ state.x le max(lim[0,*]) and $ state.y ge min(lim[1,*]) and $ state.y le max(lim[1,*]), count) j=check_math() ; suppresses math errors here. if count ne 0 then begin state.new[z]=1 endif end 2: begin ; motion plotit=0 if state.drag then begin if state.x1 ne state.lastx or state.y1 ne state.lasty then $ plots,/device,/noclip, $ [state.x1,state.x1, state.lastx,state.lastx,state.x1], $ [state.y1,state.lasty,state.lasty,state.y1, state.y1], $ color=state.color[0] plots,/device,/noclip, $ [state.x1,state.x1,event.x,event.x, state.x1], $ [state.y1,event.y, event.y,state.y1,state.y1],color=state.color[9] state.lastx = event.x state.lasty = event.y endif end else: begin message,'Unknown event',/info end endcase end state.goodid: begin z=where(state.new eq 1,count) if count ne 0 then begin state.bad[z]=0 state.new[z]=0 endif end state.sclid: begin state.scaling=event.index end state.xflipid: begin state.xflip = not state.xflip end state.yflipid: begin state.yflip = not state.yflip end else: begin message,'Unknown widget event',/info end endcase ; Replot the data if not in the middle of a drag event if not state.drag and plotit then begin markdata_plot,state endif ; Restore the state widget_control,stash,SET_UVALUE=state,/NO_COPY end ; ------------------------------------------------------------------------------ ; procedure markdata ; ------------------------------------------------------------------------------ ; This is the actual routine that creates the widget and registers it with the ; Xmanager. ; ------------------------------------------------------------------------------ pro markdata, arg1, arg2, arg3, arg4, $ GROUP = group, TITLE = title, XSIZE=xsize, YSIZE=ysize, $ XTITLE=xtitle, YTITLE=ytitle, SCALING=scaling, $ XFLIP=xflip, YFLIP=yflip, CONNECT=connect, PTITLE=ptitle common mwb_markdata,badvals if (!D.FLAGS AND 256) NE 256 then message, $ 'ERROR - Current graphics device ' + !D.NAME + ' does not support windows' if xregistered('markdata') then return if n_params() lt 2 or n_params() gt 4 then $ message,'Usage: markdata,[x],y,[err],bad' if n_params() eq 2 then begin if badpar(arg1,[2,3,4,5],1,CALLER='MARKDATA: (y) ',npts=n1) then return if badpar(arg2,[1,2,3], 1,CALLER='MARKDATA: (bad) ',npts=n2) then return npts=max([n1,n2]) endif if n_params() eq 3 then begin if badpar(arg1,[2,3,4,5],1,CALLER='MARKDATA: (x) ',npts=n1) then return if badpar(arg2,[2,3,4,5],1,CALLER='MARKDATA: (y) ',npts=n2) then return if badpar(arg3,[1,2,3], 1,CALLER='MARKDATA: (bad) ',npts=n3) then return npts=max([n1,n2,n3]) endif if n_params() eq 4 then begin if badpar(arg1,[2,3,4,5],1,CALLER='MARKDATA: (x) ' ,npts=n1) then return if badpar(arg2,[2,3,4,5],1,CALLER='MARKDATA: (y) ' ,npts=n2) then return if badpar(arg3,[2,3,4,5],1,CALLER='MARKDATA: (err) ',npts=n3) then return if badpar(arg4,[1,2,3], 1,CALLER='MARKDATA: (bad) ',npts=n4) then return npts=max([n1,n2,n3,n4]) endif if badpar(title,[0,7],0,CALLER='MARKDATA: (TITLE) ',default='Data Editor') then return if badpar(xtitle,[0,7],0,CALLER='MARKDATA: (XTITLE) ',default='x') then return if badpar(ytitle,[0,7],0,CALLER='MARKDATA: (YTITLE) ',default='y') then return if badpar(ptitle,[0,7],0,CALLER='MARKDATA: (PTITLE) ',default='') then return if badpar(xsize,[0,2,3],0,CALLER='MARKDATA: (xsize) ',default=700) then return if badpar(ysize,[0,2,3],0,CALLER='MARKDATA: (ysize) ',default=450) then return if badpar(scaling,[0,1,2,3],0,CALLER='MARKDATA: (scaling) ',default=1) then return if badpar(xflip,[0,1,2,3],0,CALLER='MARKDATA: (xflip) ',default=0) then return if badpar(yflip,[0,1,2,3],0,CALLER='MARKDATA: (yflip) ',default=0) then return if badpar(connect,[0,1,2,3],0,CALLER='MARKDATA: (connect) ',default=0) then return if !d.n_colors le 256 then begin ncolors=!d.n_colors eightbit = 1 endif else begin ncolors=256 eightbit = 0 endelse ; Save the current plotting window windo=!d.window tvlct,old_r,old_g,old_b,/get p_multi=!p.multi if connect eq 0 then connect = 1 else connect = -1 ; Setup plotting colors if eightbit then begin r=[0,255, 0, 0,255,255, 0,255,148,255] g=[0, 0,255,131,255, 0,255,161, 0,255] b=[0, 0, 0,255, 0,255,255, 0,255,255] ; 0 - black ; 1 - red ; 2 - green ; 3 - blue ; 4 - yellow ; 5 - magenta ; 6 - cyan ; 7 - orange ; 8 - violet ; 9 - white color=indgen(10) tvlct,r,g,b endif else begin color = [ $ '000000'xl, $ ; black '0000ff'xl, $ ; red '00ff00'xl, $ ; green 'ff8300'xl, $ ; blue '00ffff'xl, $ ; yellow 'ff00ff'xl, $ ; magenta 'ffff00'xl, $ ; cyan '00b3ff'xl, $ ; orange 'ff0094'xl, $ ; violet 'ffffff'xl $ ; white ] endelse !p.multi=0 ; Define the state structure if n_params() eq 2 then begin state = { $ bad: arg2, $ color: color, $ connect: connect, $ drag: 0, $ eightbit: eightbit, $ err: 0, $ errs: 0, $ lastx: 0, $ lasty: 0, $ ncolors: ncolors, $ new: intarr(npts), $ ptitle: ptitle, $ x: indgen(npts), $ x1: 0, $ x2: 0, $ xflip: xflip, $ xtitle: xtitle, $ y: arg1, $ y1: 0, $ y2: 0, $ yflip: yflip, $ ytitle: ytitle, $ allbadid: 0L, $ allid: 0L, $ badid: 0L, $ clearid: 0L, $ connectid: 0L, $ doneid: 0L, $ drawid: 0L, $ goodid: 0L, $ scaling: scaling, $ sclid: 0L, $ xflipid: 0L, $ yflipid: 0L $ } endif else if n_params() eq 3 then begin state = { $ color: color, $ ncolors: ncolors, $ ; Number of colors for display eightbit: eightbit, $ ; Flag, if true, 8-bit display drawid: 0L, $ doneid: 0L, $ badid: 0L, $ goodid: 0L, $ clearid: 0L, $ allid: 0L, $ allbadid: 0L, $ sclid: 0L, $ xflipid: 0L, $ yflipid: 0L, $ connectid: 0L, $ scaling: scaling, $ xflip: xflip, $ yflip: yflip, $ connect: connect, $ errs: 0, $ xtitle: xtitle, $ ytitle: ytitle, $ ptitle: ptitle, $ x1: 0, $ x2: 0, $ y1: 0, $ y2: 0, $ lastx: 0, $ lasty: 0, $ drag: 0, $ x: arg1, $ y: arg2, $ err: 0, $ new: intarr(npts), $ bad: arg3 $ } endif else begin state = { $ color: color, $ ncolors: ncolors, $ ; Number of colors for display eightbit: eightbit, $ ; Flag, if true, 8-bit display drawid: 0L, $ doneid: 0L, $ badid: 0L, $ goodid: 0L, $ clearid: 0L, $ allid: 0L, $ allbadid: 0L, $ sclid: 0L, $ xflipid: 0L, $ yflipid: 0L, $ connectid: 0L, $ scaling: scaling, $ xflip: xflip, $ yflip: yflip, $ connect: connect, $ errs: 1, $ xtitle: xtitle, $ ytitle: ytitle, $ ptitle: ptitle, $ x1: 0, $ x2: 0, $ y1: 0, $ y2: 0, $ lastx: 0, $ lasty: 0, $ drag: 0, $ x: arg1, $ y: arg2, $ err: arg3, $ new: intarr(npts), $ bad: arg4 $ } endelse ; Protection against NaN's z=where(finite(state.y) eq 0 and state.bad eq 0,count) if count ne 0 then begin state.bad[z]=1 print,'MARKDATA: warning, ',strcompress(count), $ ' NaN values automatically marked bad.' endif base = widget_base(TITLE=title,/COLUMN) state.drawid = widget_draw(base,XSIZE=xsize,YSIZE=ysize,/BUTTON_EVENTS,/MOTION_EVENTS) colbase = widget_base(base,/ROW) state.doneid = widget_button(colbase,VALUE=' Done ') state.badid = widget_button(colbase,VALUE=' Bad ') state.goodid = widget_button(colbase,VALUE=' Good ') state.clearid= widget_button(colbase,VALUE=' Clear ') state.allid = widget_button(colbase,VALUE=' All Good ') state.allbadid= widget_button(colbase,VALUE=' All Bad ') state.sclid = widget_droplist(colbase,VALUE=['Scale All','Scale Good']) state.xflipid= widget_button(colbase,VALUE=' Flip X ') state.yflipid= widget_button(colbase,VALUE=' Flip Y ') state.connectid= widget_button(colbase,VALUE=' Toggle Lines ') widget_control,state.sclid,SET_DROPLIST_SELECT=scaling ;Set the state. stash = widget_info(base,/CHILD) widget_control,stash,SET_UVALUE=state,/NO_COPY ;Realize the widget widget_control,base,/REALIZE ; Get the state stash=widget_info(base,/CHILD) widget_control,stash,GET_UVALUE=state,/NO_COPY ;Set the plotting window widget_control,state.drawid,get_value=dwin wset,dwin ; plot the data markdata_plot,state ;Set the state. stash = widget_info(base,/CHILD) widget_control,stash,SET_UVALUE=state,/NO_COPY xmanager,'markdata',base,EVENT_HANDLER='markdata_ev', $ GROUP_LEADER=group,/MODAL,CLEANUP='markdata_cleanup' if n_params() eq 2 then arg2=badvals if n_params() eq 3 then arg3=badvals if n_params() eq 4 then arg4=badvals wset,windo if eightbit then tvlct,old_r,old_g,old_b !p.multi=p_multi return end