;+ ; ; NAME: ; ATV ; ; PURPOSE: ; Interactive display of 2-D images. ; ; CATEGORY: ; Image display. ; ; CALLING SEQUENCE: ; atv [,array_name OR fits_file] [,min = min_value] [,max=max_value] ; [,/linear] [,/log] [,/histeq] [,/asinh] [,/block] ; [,/align] [,/stretch] [,header = header] ; ; REQUIRED INPUTS: ; None. If atv is run with no inputs, the window widgets ; are realized and images can subsequently be passed to atv ; from the command line or from the pull-down file menu. ; ; OPTIONAL INPUTS: ; array_name: a 2-D data array to display ; OR ; fits_file: a fits file name, enclosed in single quotes ; ; KEYWORDS: ; min: minimum data value to be mapped to the color table ; max: maximum data value to be mapped to the color table ; linear: use linear stretch ; log: use log stretch ; histeq: use histogram equalization ; asinh: use asinh stretch ; block: block IDL command line until ATV terminates ; align: align image with previously displayed image ; stretch: keep same min and max as previous image ; header: FITS image header (string array) for use with data array ; ; OUTPUTS: ; None. ; ; COMMON BLOCKS: ; atv_state: contains variables describing the display state ; atv_images: contains the internal copies of the display image ; atv_color: contains colormap vectors ; atv_pdata: contains plot and text annotation information ; ; RESTRICTIONS: ; Requires IDL version 6.0 or greater. ; Requires Craig Markwardt's cmps_form.pro routine. ; Requires the GSFC IDL astronomy user's library routines. ; Some features may not work under all operating systems. ; ; EXAMPLE: ; To start atv running, just enter the command 'atv' at the idl ; prompt, either with or without an array name or fits file name ; as an input. Only one atv window will be created at a time, ; so if one already exists and another image is passed to atv ; from the idl command line, the new image will be displayed in ; the pre-existing atv window. ; ; MODIFICATION HISTORY: ; Written/maintained by Aaron J. Barth, with contributions by ; numerous others. First released 17 December 1998. ; ; This version is 2.0b4, last modified November 27 2007. ; ; For the most current version, revision history, instructions, ; list of known bugs, and further information, go to: ; http://www.physics.uci.edu/~barth/atv ; ;- ;---------------------------------------------------------------------- ; atv startup, initialization, and shutdown routines ;---------------------------------------------------------------------- pro atv_initcommon ; Routine to initialize the atv common blocks. Use common blocks so ; that other IDL programs can access the atv internal data easily. common atv_state, state common atv_color, r_vector, g_vector, b_vector, user_r, user_g, user_b common atv_pdata, nplot, maxplot, plot_ptr common atv_images, $ main_image, $ display_image, $ scaled_image, $ blink_image1, $ blink_image2, $ blink_image3, $ unblink_image, $ pan_image state = { $ version: '2.0b4', $ ; version # of this release head_ptr: ptr_new(), $ ; pointer to image header astr_ptr: ptr_new(), $ ; pointer to astrometry info structure firstimage: 1, $ ; is this the first image? block: 0, $ ; are we in blocking mode? wcstype: 'none', $ ; coord info type (none/angle/lambda) equinox: 'J2000', $ ; equinox of coord system display_coord_sys: 'RA--', $ ; coord system displayed display_equinox: 'J2000', $ ; equinox of displayed coords display_base60: 1B, $ ; Display RA,dec in base 60? cunit: '', $ ; wavelength units imagename: '', $ ; image file name title_extras: '', $ ; extras for image title bitdepth: 8, $ ; 8 or 24 bit color mode? screen_xsize: 1000, $ ; horizontal size of screen screen_ysize: 1000, $ ; vertical size of screen base_id: 0L, $ ; id of top-level base base_min_size: [512L, 300L], $ ; min size for top-level base draw_base_id: 0L, $ ; id of base holding draw window draw_window_id: 0L, $ ; window id of draw window draw_widget_id: 0L, $ ; widget id of draw widget mousemode: "color", $ ; color, blink, zoom, or imexam mode_droplist_id: 0L, $ ; id of mode droplist widget track_window_id: 0L, $ ; widget id of tracking window pan_widget_id: 0L, $ ; widget id of pan window pan_window_id: 0L, $ ; window id of pan window active_window_id: 0L, $ ; user's active window outside atv active_window_pmulti: lonarr(5), $ ; user's active window p.multi info_base_id: 0L, $ ; id of base holding info bars location_bar_id: 0L, $ ; id of (x,y,value) label wcs_bar_id: 0L, $ ; id of WCS label widget min_text_id: 0L, $ ; id of min= widget max_text_id: 0L, $ ; id of max= widget menu_ids: lonarr(35), $ ; list of top menu items colorbar_base_id: 0L, $ ; id of colorbar base widget colorbar_widget_id: 0L, $ ; widget id of colorbar draw widget colorbar_window_id: 0L, $ ; window id of colorbar colorbar_height: 6L, $ ; height of colorbar in pixels ncolors: 0B, $ ; image colors (!d.table_size - 9) box_color: 2, $ ; color for pan box and zoom x brightness: 0.5, $ ; initial brightness setting contrast: 0.5, $ ; initial contrast setting image_min: 0.0, $ ; min(main_image) image_max: 0.0, $ ; max(main_image) min_value: 0.0, $ ; min data value mapped to colors max_value: 0.0, $ ; max data value mapped to colors skymode: 0.0, $ ; sky mode value skysig: 0.0, $ ; sky sigma value draw_window_size: [512L, 512L], $ ; size of main draw window track_window_size: 121L, $ ; size of tracking window pan_window_size: 121L, $ ; size of pan window pan_scale: 0.0, $ ; magnification of pan image image_size: [0L,0L], $ ; size of main_image invert_colormap: 0L, $ ; 0=normal, 1=inverted coord: [0L, 0L], $ ; cursor position in image coords scaling: 3, $ ; 0=lin,1=log,2=histeq,3=asinh asinh_beta: 0.1, $ ; asinh nonlinearity parameter offset: [0L, 0L], $ ; offset to viewport coords base_pad: [0L, 0L], $ ; padding around draw base zoom_level: 0L, $ ; integer zoom level, 0=normal zoom_factor: 1.0, $ ; magnification factor = 2^zoom_level centerpix: [0L, 0L], $ ; pixel at center of viewport cstretch: 0B, $ ; flag = 1 while stretching colors pan_offset: [0L, 0L], $ ; image offset in pan window frame: 1L, $ ; put frame around ps output? framethick: 6, $ ; thickness of frame plot_coord: [0L, 0L], $ ; cursor position for a plot vector_coord1: [0L, 0L], $ ; 1st cursor position in vector plot vector_coord2: [0L, 0L], $ ; 2nd cursor position in vector plot vector_pixmap_id: 0L, $ ; id for vector pixmap vectorpress: 0L, $ ; are we plotting a vector? vectorstart: [0L,0L], $ ; device x,y of vector start plot_type:'', $ ; plot type for plot window lineplot_widget_id: 0L, $ ; id of lineplot widget lineplot_window_id: 0L, $ ; id of lineplot window lineplot_base_id: 0L, $ ; id of lineplot top-level base lineplot_size: [600L, 500L], $ ; size of lineplot window lineplot_min_size: [100L, 0L], $ ; min size of lineplot window lineplot_pad: [0L, 0L], $ ; padding around lineplot window lineplot_xmin_id: 0L, $ ; id of xmin for lineplot windows lineplot_xmax_id: 0L, $ ; id of xmax for lineplot windows lineplot_ymin_id: 0L, $ ; id of ymin for lineplot windows lineplot_ymax_id: 0L, $ ; id of ymax for lineplot windows lineplot_xmin: 0.0, $ ; xmin for lineplot windows lineplot_xmax: 0.0, $ ; xmax for lineplot windows lineplot_ymin: 0.0, $ ; ymin for lineplot windows lineplot_ymax: 0.0, $ ; ymax for lineplot windows lineplot_xmin_orig: 0.0, $ ; original xmin saved from histplot lineplot_xmax_orig: 0.0, $ ; original xmax saved from histplot holdrange_base_id: 0L, $ ; base id for 'Hold Range' button holdrange_button_id: 0L, $ ; button id for 'Hold Range' button holdrange_value: 0, $ ; 0=HoldRange Off, 1=HoldRange On histbutton_base_id: 0L, $ ; id of histogram button base histplot_binsize_id: 0L, $ ; id of binsize for histogram plot x1_pix_id: 0L, $ ; id of x1 pixel for histogram plot x2_pix_id: 0L, $ ; id of x2 pixel for histogram plot y1_pix_id: 0L, $ ; id of y1 pixel for histogram plot y2_pix_id: 0L, $ ; id of y2 pixel for histogram plot binsize: 0.0, $ ; binsize for histogram plots regionform_id: 0L, $ ; id of region form widget reg_ids_ptr: ptr_new(), $ ; ids for region form widget ; writeimage_ids_ptr: ptr_new(),$ ; ids for writeimage form widget ; writeformat: 'PNG', $ ; default format for WriteImage cursorpos: lonarr(2), $ ; cursor x,y for photometry & stats centerpos: fltarr(2), $ ; centered x,y for photometry cursorpos_id: 0L, $ ; id of cursorpos widget centerpos_id: 0L, $ ; id of centerpos widget centerbox_id: 0L, $ ; id of centeringboxsize widget radius_id: 0L, $ ; id of radius widget innersky_id: 0L, $ ; id of inner sky widget outersky_id: 0L, $ ; id of outer sky widget magunits: 0, $ ; 0=counts, 1=magnitudes skytype: 0, $ ; 0=idlphot,1=median,2=no sky subtract photzpt: 25.0, $ ; magnitude zeropoint skyresult_id: 0L, $ ; id of sky widget photresult_id: 0L, $ ; id of photometry result widget fwhm_id: 0L, $ ; id of fwhm widget radplot_widget_id: 0L, $ ; id of radial profile widget radplot_window_id: 0L, $ ; id of radial profile window photzoom_window_id: 0L, $ ; id of photometry zoom window photzoom_size: 190L, $ ; size in pixels of photzoom window showradplot_id: 0L, $ ; id of button to show/hide radplot photwarning_id: 0L, $ ; id of photometry warning widget photwarning: ' ', $ ; photometry warning text centerboxsize: 9L, $ ; centering box size aprad: 5.0, $ ; aperture photometry radius innersky: 10.0, $ ; inner sky radius outersky: 20.0, $ ; outer sky radius headinfo_base_id: 0L, $ ; headinfo base widget id pixtable_base_id: 0L, $ ; pixel table base widget id pixtable_tbl_id: 0L, $ ; pixel table widget_table id stats_base_id: 0L, $ ; base widget for image stats statboxsize: 11L, $ ; box size for computing statistics statbox_id: 0L, $ ; widget id for stat box size stat_npix_id: 0L, $ ; widget id for # pixels in stats box statxcenter_id: 0L, $ ; widget id for stat box x center statycenter_id: 0L, $ ; widget id for stat box y center statbox_min_id: 0L, $ ; widget id for stat min box statbox_max_id: 0L, $ ; widget id for stat max box statbox_mean_id: 0L, $ ; widget id for stat mean box statbox_median_id: 0L, $ ; widget id for stat median box statbox_stdev_id: 0L, $ ; widget id for stat stdev box statzoom_size: 300, $ ; size of statzoom window statzoom_widget_id: 0L, $ ; widget id for stat zoom window statzoom_window_id: 0L, $ ; window id for stat zoom window showstatzoom_id: 0L, $ ; widget id for show/hide button pan_pixmap: 0L, $ ; window id of pan pixmap current_dir: '', $ ; current readfits directory graphicsdevice: '', $ ; screen device ispsformon: 0, $ ; is cmps_form running? newrefresh: 0, $ ; refresh since last blink? blinks: 0B, $ ; remembers which images are blinked activator: 0, $ ; is "activator" mode on? delimiter: '/', $ ; filesystem level delimiter default_align: 1, $ ; align next image by default? default_autoscale: 1, $ ; autoscale images by default? default_stretch: 0 $ ; use previous minmax for new image? } nplot = 0 maxplot = 5000 plot_ptr = ptrarr(maxplot+1) ; The 0th element isn't used. blink_image1 = 0 blink_image2 = 0 blink_image3 = 0 end ;--------------------------------------------------------------------- pro atv_startup ; This routine initializes the atv internal variables, and creates and ; realizes the window widgets. It is only called by the atv main ; program level, when there is no previously existing atv window. common atv_state common atv_color ; common block atvcompileoptions is only used by my script to compile ; a version of atv for the idl virtual machine. ; save the user color table and pmulti first tvlct, user_r, user_g, user_b, /get ; Read in a color table to initialize !d.table_size ; As a bare minimum, we need the 8 basic colors used by ATV_ICOLOR(), ; plus 2 more for a color map. ;loadct, 0, /silent if (!d.table_size LT 12) then begin message, 'Too few colors available for color table' tvlct, user_r, user_g, user_b atv_shutdown endif ; Initialize the common blocks atv_initcommon state.active_window_pmulti = !p.multi !p.multi = 0 osfamily = strupcase(!version.os_family) case osfamily of 'UNIX': state.delimiter = '/' 'WINDOWS': state.delimiter = '\' else: endcase state.ncolors = !d.table_size - 9 ; If compiling atv to make a sav file for the atv virtual machine, ; always do it for 24-bit color with retain & decomposed set. ; Uncomment this block to compile atv for idl vm. For some reason, ; idl vm gets !d.n_colors=256 even on a 24-bit display, so we need ; this to work around it to force 24-bit mode. ;device, true_color=24 ;device, decomposed=0 ;device, retain=2 ;state.bitdepth=24 ; For normal idl operation, use the following. Comment this block out ; if compiling atv for idl vm. if (!d.n_colors LE 256) then begin state.bitdepth = 8 endif else begin state.bitdepth = 24 device, decomposed=0 endelse state.graphicsdevice = !d.name state.screen_xsize = (get_screen_size())[0] state.screen_ysize = (get_screen_size())[1] ; Get the current window id and color table atv_getwindow ; Define the widgets. For the widgets that need to be modified later ; on, save their widget ids in state variables base = widget_base(title = 'atv', $ /column, /base_align_right, $ app_mbar = top_menu, $ uvalue = 'atv_base', $ /tlb_size_events) state.base_id = base tmp_struct = {cw_pdmenu_s, flags:0, name:''} top_menu_desc = [ $ {cw_pdmenu_s, 1, 'File'}, $ ; file menu {cw_pdmenu_s, 0, 'ReadFits'}, $ {cw_pdmenu_s, 0, 'WriteFits'}, $ {cw_pdmenu_s, 0, 'WritePS'}, $ {cw_pdmenu_s, 1, 'WriteImage'}, $ {cw_pdmenu_s, 0, 'PNG'}, $ {cw_pdmenu_s, 0, 'JPEG'}, $ {cw_pdmenu_s, 2, 'TIFF'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 1, 'GetImage'}, $ {cw_pdmenu_s, 0, ' DSS'}, $ {cw_pdmenu_s, 2, ' FIRST'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 2, 'Quit'}, $ {cw_pdmenu_s, 1, 'ColorMap'}, $ ; color menu {cw_pdmenu_s, 0, 'Grayscale'}, $ {cw_pdmenu_s, 0, 'Blue-White'}, $ {cw_pdmenu_s, 0, 'Red-Orange'}, $ {cw_pdmenu_s, 0, 'Green-White'}, $ {cw_pdmenu_s, 0, 'Rainbow'}, $ {cw_pdmenu_s, 0, 'BGRY'}, $ {cw_pdmenu_s, 0, 'Stern Special'}, $ {cw_pdmenu_s, 0, 'ATV Special'}, $ {cw_pdmenu_s, 0, 'Velocity1'}, $ {cw_pdmenu_s, 2, 'Velocity2'}, $ {cw_pdmenu_s, 1, 'Scaling'}, $ ; scaling menu {cw_pdmenu_s, 0, 'Asinh'}, $ {cw_pdmenu_s, 0, 'Log'}, $ {cw_pdmenu_s, 0, 'Linear'}, $ {cw_pdmenu_s, 0, 'HistEq'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 2, 'Asinh Settings'}, $ {cw_pdmenu_s, 1, 'Labels'}, $ ; labels menu {cw_pdmenu_s, 0, 'TextLabel'}, $ {cw_pdmenu_s, 0, 'Arrow'}, $ {cw_pdmenu_s, 0, 'Contour'}, $ {cw_pdmenu_s, 0, 'Compass'}, $ {cw_pdmenu_s, 0, 'ScaleBar'}, $ {cw_pdmenu_s, 0, 'Region'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 0, 'EraseLast'}, $ {cw_pdmenu_s, 0, 'EraseAll'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 0, 'LoadRegions'}, $ {cw_pdmenu_s, 2, 'SaveRegions'}, $ {cw_pdmenu_s, 1, 'Blink'}, $ {cw_pdmenu_s, 0, 'SetBlink1'}, $ {cw_pdmenu_s, 0, 'SetBlink2'}, $ {cw_pdmenu_s, 0, 'SetBlink3'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 2, 'MakeRGB'}, $ {cw_pdmenu_s, 1, 'Rotate/Zoom'}, $ {cw_pdmenu_s, 0, 'Rotate'}, $ {cw_pdmenu_s, 0, '90 deg'}, $ {cw_pdmenu_s, 0, '180 deg'}, $ {cw_pdmenu_s, 0, '270 deg'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 0, 'Invert X'}, $ {cw_pdmenu_s, 0, 'Invert Y'}, $ {cw_pdmenu_s, 0, 'Invert XY'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 0, '1/16x'}, $ {cw_pdmenu_s, 0, '1/8x'}, $ {cw_pdmenu_s, 0, '1/4x'}, $ {cw_pdmenu_s, 0, '1/2x'}, $ {cw_pdmenu_s, 0, '1x'}, $ {cw_pdmenu_s, 0, '2x'}, $ {cw_pdmenu_s, 0, '4x'}, $ {cw_pdmenu_s, 0, '8x'}, $ {cw_pdmenu_s, 2, '16x'}, $ {cw_pdmenu_s, 1, 'ImageInfo'}, $ ;info menu {cw_pdmenu_s, 0, 'ImageHeader'}, $ {cw_pdmenu_s, 0, 'Photometry'}, $ {cw_pdmenu_s, 0, 'Statistics'}, $ {cw_pdmenu_s, 0, 'PixelTable'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 0, 'RA,dec (J2000)'}, $ {cw_pdmenu_s, 0, 'RA,dec (B1950)'}, $ {cw_pdmenu_s, 0, '--------------'}, $ {cw_pdmenu_s, 0, 'RA,dec (J2000) deg'}, $ {cw_pdmenu_s, 0, 'Galactic'}, $ {cw_pdmenu_s, 0, 'Ecliptic (J2000)'}, $ {cw_pdmenu_s, 2, 'Native'}, $ {cw_pdmenu_s, 1, 'Help'}, $ ; help menu {cw_pdmenu_s, 2, 'ATV Help'} $ ] top_menu = cw_pdmenu(top_menu, top_menu_desc, $ ids = state.menu_ids, $ /mbar, $ /help, $ /return_name, $ uvalue = 'top_menu') track_base = widget_base(base, /row) state.info_base_id = widget_base(track_base, /column, /base_align_right) buttonbar_base = widget_base(base, column=2) state.draw_base_id = widget_base(base, $ /column, /base_align_left, $ uvalue = 'draw_base', $ frame = 2) state.colorbar_base_id = widget_base(base, $ uvalue = 'cqolorbar_base', $ /column, /base_align_left, $ frame = 2) state.min_text_id = cw_field(state.info_base_id, $ uvalue = 'min_text', $ /floating, $ title = 'Min=', $ value = state.min_value, $ /return_events, $ xsize = 12) state.max_text_id = cw_field(state.info_base_id, $ uvalue = 'max_text', $ /floating, $ title = 'Max=', $ value = state.max_value, $ /return_events, $ xsize = 12) tmp_string = string(1000, 1000, 1.0e-10, $ format = '("(",i5,",",i5,") ",g12.5)' ) state.location_bar_id = widget_label (state.info_base_id, $ value = tmp_string, $ uvalue = 'location_bar', frame = 1) tmp_string = string(12, 12, 12.001, -60, 60, 60.01, ' J2000', $ format = '(i2,":",i2,":",f6.3," ",i3,":",i2,":",f5.2," ",a6)' ) state.wcs_bar_id = widget_label (state.info_base_id, $ value = tmp_string, $ uvalue = 'wcs_bar', frame = 1) state.pan_widget_id = widget_draw(track_base, $ xsize = state.pan_window_size, $ ysize = state.pan_window_size, $ frame = 2, uvalue = 'pan_window', $ /button_events, /motion_events) track_window = widget_draw(track_base, $ xsize=state.track_window_size, $ ysize=state.track_window_size, $ frame=2, uvalue='track_window') modebase = widget_base(buttonbar_base, /row, /base_align_center) modelist = ['Color', 'Zoom', 'Blink', 'ImExam', 'Vector'] mode_droplist_id = widget_droplist(modebase, $ frame = 1, $ title = 'MouseMode:', $ uvalue = 'mode', $ value = modelist) state.mode_droplist_id = mode_droplist_id button_base = widget_base(buttonbar_base, row=2) invert_button = widget_button(button_base, $ value = 'Invert', $ uvalue = 'invert') restretch_button = widget_button(button_base, $ value = 'Restretch', $ uvalue = 'restretch_button') autoscale_button = widget_button(button_base, $ uvalue = 'autoscale_button', $ value = 'AutoScale') fullrange_button = widget_button(button_base, $ uvalue = 'full_range', $ value = 'FullRange') dummy_spacing_widget = widget_label(button_base,value='') zoomin_button = widget_button(button_base, $ value = 'ZoomIn', $ uvalue = 'zoom_in') zoomout_button = widget_button(button_base, $ value = 'ZoomOut', $ uvalue = 'zoom_out') zoomone_button = widget_button(button_base, $ value = 'Zoom1', $ uvalue = 'zoom_one') fullview_button = widget_button(button_base, $ value = 'FullView', $ uvalue = 'fullview') center_button = widget_button(button_base, $ value = 'Center', $ uvalue = 'center') ;done_button = widget_button(button_base, $ ; value = 'Done', $ ; uvalue = 'done') ; Set widget y size for small screens state.draw_window_size[1] = state.draw_window_size[1] < $ (state.screen_ysize - 300) state.draw_widget_id = widget_draw(state.draw_base_id, $ uvalue = 'draw_window', $ /motion_events, /button_events, $ keyboard_events=2, $ scr_xsize = state.draw_window_size[0], $ scr_ysize = state.draw_window_size[1]) state.colorbar_widget_id = widget_draw(state.colorbar_base_id, $ uvalue = 'colorbar', $ scr_xsize = state.draw_window_size[0], $ scr_ysize = state.colorbar_height) ; Create the widgets on screen widget_control, base, /realize widget_control, state.pan_widget_id, draw_motion_events = 0 ; get the window ids for the draw widgets widget_control, track_window, get_value = tmp_value state.track_window_id = tmp_value widget_control, state.draw_widget_id, get_value = tmp_value state.draw_window_id = tmp_value widget_control, state.pan_widget_id, get_value = tmp_value state.pan_window_id = tmp_value widget_control, state.colorbar_widget_id, get_value = tmp_value state.colorbar_window_id = tmp_value ; set the event handlers widget_control, top_menu, event_pro = 'atv_topmenu_event' widget_control, state.draw_widget_id, event_pro = 'atv_draw_event' widget_control, state.pan_widget_id, event_pro = 'atv_pan_event' ; Find window padding sizes needed for resizing routines. ; Add extra padding for menu bar, since this isn't included in ; the geometry returned by widget_info. ; Also add extra padding for margin (frame) in draw base. basegeom = widget_info(state.base_id, /geometry) drawbasegeom = widget_info(state.draw_base_id, /geometry) ; Initialize the vectors that hold the current color table. ; See the routine atv_stretchct to see why we do it this way. r_vector = bytarr(state.ncolors) g_vector = bytarr(state.ncolors) b_vector = bytarr(state.ncolors) atv_getct, 0 state.invert_colormap = 0 ; Create a pixmap window to hold the pan image window, /free, xsize=state.pan_window_size, ysize=state.pan_window_size, $ /pixmap state.pan_pixmap = !d.window atv_resetwindow atv_colorbar widget_control, state.base_id, tlb_get_size=tmp_event state.base_pad = tmp_event - state.draw_window_size end ;-------------------------------------------------------------------- pro atv_colorbar ; Routine to tv the colorbar at the bottom of the atv window common atv_state atv_setwindow, state.colorbar_window_id xsize = (widget_info(state.colorbar_widget_id, /geometry)).xsize b = congrid( findgen(state.ncolors), xsize) + 8 c = replicate(1, state.colorbar_height) a = b # c tv, a atv_resetwindow end ;------------------------------------------------------------------- pro atvclear ; displays a small blank image, useful for clearing memory if atv is ; displaying a huge image. atv, fltarr(10,10) end ;-------------------------------------------------------------------- pro atv_shutdown, windowid ; routine to kill the atv window(s) and clear variables to conserve ; memory when quitting atv. The windowid parameter is used when ; atv_shutdown is called automatically by the xmanager, if atv is ; killed by the window manager. common atv_images common atv_state common atv_color common atv_pdata ; reset color table and pmulti to user values tvlct, user_r, user_g, user_b !p.multi = state.active_window_pmulti ; Kill top-level base if it still exists if (xregistered ('atv')) then widget_control, state.base_id, /destroy ; Destroy all pointers to plots and their heap variables: this runs ; ptr_free on any existing plot pointers if (nplot GT 0) then begin atverase, /norefresh endif if (size(state, /tname) EQ 'STRUCT') then begin if (!d.name EQ state.graphicsdevice) then wdelete, state.pan_pixmap if (ptr_valid(state.head_ptr)) then ptr_free, state.head_ptr if (ptr_valid(state.astr_ptr)) then ptr_free, state.astr_ptr endif ; Clean up saved variables in common blocks to conserve memory. ; Previously this was done using delvarx, but since delvarx uses an ; execute function, it's incompatible with IDL virtual machine. So, ; just set these variables to zero. plot_ptr=0 maxplot=0 main_image=0 display_image=0 scaled_image=0 blink_image1=0 blink_image2=0 blink_image3=0 unlink_image=0 pan_image=0 r_vector=0 g_vector=0 b_vector=0 user_r=0 user_g=0 user_b=0 state=0 return end ;-------------------------------------------------------------------- ; main atv event loops ;-------------------------------------------------------------------- pro atv_topmenu_event, event ; Event handler for top menu common atv_state common atv_images widget_control, event.id, get_uvalue = event_name if (!d.name NE state.graphicsdevice and event_name NE 'Quit') then return if (state.bitdepth EQ 24) then true = 1 else true = 0 ; Need to get active window here in case mouse goes to menu from top ; of atv window without entering the main base atv_getwindow case event_name of ; File menu options: 'ReadFits': begin atv_readfits, newimage=newimage if (newimage EQ 1) then begin atv_getstats, align=state.default_align if (state.default_align EQ 0) then begin state.zoom_level = 0 state.zoom_factor = 1.0 endif if (state.default_stretch EQ 0 AND $ state.default_autoscale EQ 1) then atv_autoscale if (state.firstimage EQ 1) then atv_autoscale atv_set_minmax atv_displayall atv_settitle state.firstimage = 0 endif end 'WriteFits': atv_writefits 'WritePS' : atv_writeps ; 'WriteImage': atv_writeimage 'PNG': atv_writeimage, 'png' 'JPEG': atv_writeimage, 'jpg' 'TIFF': atv_writeimage, 'tiff' 'GetImage': ' DSS': atv_getdss ' FIRST': atv_getfirst 'LoadRegions': atv_loadregion 'SaveRegions': atv_saveregion 'Quit': if (state.activator EQ 0) then atv_shutdown $ else state.activator = 0 ; ColorMap menu options: 'Grayscale': atv_getct, 0 'Blue-White': atv_getct, 1 'Red-Orange': atv_getct, 3 'BGRY': atv_getct, 4 'Rainbow': atv_getct, 13 'Stern Special': atv_getct, 15 'Green-White': atv_getct, 8 'ATV Special': atv_makect, event_name 'Velocity1': atv_makect, event_name 'Velocity2': atv_makect, event_name ; Scaling options: 'Linear': begin state.scaling = 0 atv_displayall end 'Log': begin state.scaling = 1 atv_displayall end 'HistEq': begin state.scaling = 2 atv_displayall end 'Asinh': begin state.scaling = 3 atv_displayall end 'Asinh Settings': begin atv_setasinh end ; Label options: 'TextLabel': atv_textlabel 'Arrow': atv_setarrow 'Contour': atv_oplotcontour 'Compass': atv_setcompass 'ScaleBar': atv_setscalebar 'Region': atv_setregion 'EraseLast': atverase, 1 'EraseAll': atverase ; Blink options: 'SetBlink1': begin atv_setwindow, state.draw_window_id blink_image1 = tvrd(true = true) end 'SetBlink2': begin atv_setwindow, state.draw_window_id blink_image2 = tvrd(true = true) end 'SetBlink3': begin atv_setwindow, state.draw_window_id blink_image3 = tvrd(true = true) end 'MakeRGB' : atv_makergb ; Zoom/Rotate options '1/16x': atv_zoom, 'onesixteenth' '1/8x': atv_zoom, 'oneeighth' '1/4x': atv_zoom, 'onefourth' '1/2x': atv_zoom, 'onehalf' '1x': atv_zoom, 'one' '2x': atv_zoom, 'two' '4x': atv_zoom, 'four' '8x': atv_zoom, 'eight' '16x': atv_zoom, 'sixteen' 'Invert X': atv_invert, 'x' 'Invert Y': atv_invert, 'y' 'Invert XY': atv_invert, 'xy' 'Rotate': atv_rotate, '0', /get_angle '0 deg': atv_rotate, '0' '90 deg': atv_rotate, '90' '180 deg': atv_rotate, '180' '270 deg': atv_rotate, '270' ; Info options: 'Photometry': atv_apphot 'ImageHeader': atv_headinfo 'Statistics': atv_showstats 'PixelTable': atv_pixtable ; Coordinate system options: '--------------': 'RA,dec (J2000)': BEGIN state.display_coord_sys = 'RA--' state.display_equinox = 'J2000' state.display_base60 = 1B atv_gettrack ; refresh coordinate window END 'RA,dec (B1950)': BEGIN state.display_coord_sys = 'RA--' state.display_equinox = 'B1950' state.display_base60 = 1B atv_gettrack ; refresh coordinate window END 'RA,dec (J2000) deg': BEGIN state.display_coord_sys = 'RA--' state.display_equinox = 'J2000' state.display_base60 = 0B atv_gettrack ; refresh coordinate window END 'Galactic': BEGIN state.display_coord_sys = 'GLON' atv_gettrack ; refresh coordinate window END 'Ecliptic (J2000)': BEGIN state.display_coord_sys = 'ELON' state.display_equinox = 'J2000' atv_gettrack ; refresh coordinate window END 'Native': BEGIN IF (state.wcstype EQ 'angle') THEN BEGIN state.display_coord_sys = strmid((*state.astr_ptr).ctype[0], 0, 4) state.display_equinox = state.equinox atv_gettrack ; refresh coordinate window ENDIF END ; Help options: 'ATV Help': atv_help else: print, 'Unknown event in file menu!' endcase ; Need to test whether atv is still alive, since the quit option ; might have been selected. if (xregistered('atv', /noshow)) then atv_resetwindow end ;-------------------------------------------------------------------- pro atv_draw_event, event ; top-level event handler for draw widget events common atv_state if (!d.name NE state.graphicsdevice) then return if (event.type EQ 0 or event.type EQ 1 or event.type EQ 2) then begin case state.mousemode of 'color': atv_draw_color_event, event 'zoom': atv_draw_zoom_event, event 'blink': atv_draw_blink_event, event 'imexam': atv_draw_phot_event, event 'vector': atv_draw_vector_event, event endcase endif if (event.type EQ 5 or event.type EQ 6) then $ atv_draw_keyboard_event, event if (xregistered('atv', /noshow)) then $ widget_control, state.draw_widget_id, /sensitive, /input_focus end ;-------------------------------------------------------------------- pro atv_draw_color_event, event ; Event handler for color mode common atv_state common atv_images ;if (!d.name NE state.graphicsdevice) then return case event.type of 0: begin ; button press if (event.press EQ 1) then begin state.cstretch = 1 atv_stretchct, event.x, event.y, /getcursor atv_resetwindow atv_colorbar endif else begin atv_zoom, 'none', /recenter endelse end 1: begin state.cstretch = 0 ; button release if (state.bitdepth EQ 24) then atv_refresh atv_draw_motion_event, event end 2: begin ; motion event if (state.cstretch EQ 1) then begin atv_stretchct, event.x, event.y, /getcursor atv_resetwindow if (state.bitdepth EQ 24) then atv_refresh, /fast endif else begin atv_draw_motion_event, event endelse end endcase widget_control, state.draw_widget_id, /sensitive, /input_focus end ;-------------------------------------------------------------------- pro atv_draw_keyboard_event, event common atv_state common atv_images common atv_color ; Only want to look for key presses, not key releases. if (event.release EQ 1) then return if (event.type EQ 5) then begin eventchar = string(event.ch) if (!d.name NE state.graphicsdevice and eventchar NE 'q') then return if (state.bitdepth EQ 24) then true = 1 else true = 0 case eventchar of '1': atv_move_cursor, eventchar '2': atv_move_cursor, eventchar '3': atv_move_cursor, eventchar '4': atv_move_cursor, eventchar '6': atv_move_cursor, eventchar '7': atv_move_cursor, eventchar '8': atv_move_cursor, eventchar '9': atv_move_cursor, eventchar 'r': atv_rowplot, /newcoord 'c': atv_colplot, /newcoord 's': atv_surfplot, /newcoord 't': atv_contourplot, /newcoord 'h': atv_histplot, /newcoord 'p': atv_apphot 'i': atv_showstats 'm': atv_changemode '!': begin atv_setwindow, state.draw_window_id blink_image1 = tvrd(true = true) atv_resetwindow end '@': begin atv_setwindow, state.draw_window_id blink_image2 = tvrd(true = true) atv_resetwindow end '#': begin atv_setwindow, state.draw_window_id blink_image3 = tvrd(true = true) atv_resetwindow end 'q': if (state.activator EQ 0) then atv_shutdown $ else state.activator = 0 'Q': if (state.activator EQ 0) then atv_shutdown $ else state.activator = 0 else: ;any other key press does nothing endcase endif ; Starting with IDL 6.0, can generate events on arrow keys: if (event.type EQ 6) then begin case event.key of 5: atv_move_cursor, '4' 6: atv_move_cursor, '6' 7: atv_move_cursor, '8' 8: atv_move_cursor, '2' else: endcase endif if (xregistered('atv', /noshow)) then $ widget_control, state.draw_widget_id, /sensitive, /input_focus end ;------------------------------------------------------------------- pro atv_activate ; This routine is a workaround to use when you hit an error message or ; a "stop" command in another program while running atv. If you want ; atv to become active again without typing "retall" and losing your ; current session variables, type "atv_activate" to temporarily ; activate atv again. This will de-activate the command line but ; allow atv to be used until you hit "q" or click "done" in atv. ; Also, if you need to call atv from a command-line idl program and ; have that program wait until you're done looking at an image in atv ; before moving on to its next step, you can call atv_activate after ; sending your image to atv. This will make your external program ; stop until you quit out of atv_activate mode. common atv_state if (not(xregistered('atv', /noshow))) then begin print, 'No ATV window currently exists.' return endif state.activator = 1 activator = 1 while (activator EQ 1) do begin wait, 0.01 void = widget_event(/nowait) ; If atv is killed by the window manager, then by the time we get here ; the state structure has already been destroyed by atv_shutdown. if (size(state, /type) NE 8) then begin activator = 0 endif else begin activator = state.activator endelse endwhile widget_control, /hourglass end ;------------------------------------------------------------------- pro atv_changemode ; Use 'm' keypress to cycle through mouse modes common atv_state case state.mousemode of 'color': begin state.mousemode = 'zoom' widget_control, state.mode_droplist_id, set_droplist_select=1 end 'zoom': begin state.mousemode = 'blink' widget_control, state.mode_droplist_id, set_droplist_select=2 end 'blink': begin state.mousemode = 'imexam' widget_control, state.mode_droplist_id, set_droplist_select=3 end 'imexam': begin state.mousemode = 'vector' widget_control, state.mode_droplist_id, set_droplist_select=4 end 'vector': begin state.mousemode = 'color' widget_control, state.mode_droplist_id, set_droplist_select=0 end endcase end ;------------------------------------------------------------------ pro atv_draw_zoom_event, event ; Event handler for zoom mode common atv_state if (!d.name NE state.graphicsdevice) then return if (event.type EQ 0) then begin case event.press of 1: atv_zoom, 'in', /recenter 2: atv_zoom, 'none', /recenter 4: atv_zoom, 'out', /recenter endcase endif if (event.type EQ 2) then atv_draw_motion_event, event if (xregistered('atv', /noshow)) then $ widget_control, state.draw_widget_id, /sensitive, /input_focus end ;--------------------------------------------------------------------- pro atv_draw_blink_event, event ; Event handler for blink mode common atv_state common atv_images if (!d.name NE state.graphicsdevice) then return if (state.bitdepth EQ 24) then true = 1 else true = 0 case event.type of 0: begin ; button press atv_setwindow, state.draw_window_id ; define the unblink image if needed if ((state.newrefresh EQ 1) AND (state.blinks EQ 0)) then begin unblink_image = tvrd(true = true) state.newrefresh = 0 endif case event.press of 1: if n_elements(blink_image1) GT 1 then $ tv, blink_image1, true = true 2: if n_elements(blink_image2) GT 1 then $ tv, blink_image2, true = true 4: if n_elements(blink_image3) GT 1 then $ tv, blink_image3, true = true else: event.press = 0 ; in case of errors endcase state.blinks = (state.blinks + event.press) < 7 end 1: begin ; button release if (n_elements(unblink_image) EQ 0) then return ; just in case atv_setwindow, state.draw_window_id state.blinks = (state.blinks - event.release) > 0 case state.blinks of 0: tv, unblink_image, true = true 1: if n_elements(blink_image1) GT 1 then $ tv, blink_image1, true = true else $ tv, unblink_image, true = true 2: if n_elements(blink_image2) GT 1 then $ tv, blink_image2, true = true else $ tv, unblink_image, true = true 3: if n_elements(blink_image1) GT 1 then begin tv, blink_image1, true = true endif else if n_elements(blink_image2) GT 1 then begin tv, blink_image2, true = true endif else begin tv, unblink_image, true = true endelse 4: if n_elements(blink_image3) GT 1 then $ tv, blink_image3, true = true $ else tv, unblink_image, true = true 5: if n_elements(blink_image1) GT 1 then begin tv, blink_image1, true = true endif else if n_elements(blink_image3) GT 1 then begin tv, blink_image3, true = true endif else begin tv, unblink_image, true = true endelse 6: if n_elements(blink_image2) GT 1 then begin tv, blink_image2, true = true endif else if n_elements(blink_image4) GT 1 then begin tv, blink_image4, true = true endif else begin tv, unblink_image, true = true endelse else: begin ; check for errors state.blinks = 0 tv, unblink_image, true = true end endcase end 2: atv_draw_motion_event, event ; motion event endcase widget_control, state.draw_widget_id, /sensitive, /input_focus atv_resetwindow end ;------------------------------------------------------------------- pro atv_draw_phot_event, event ; Event handler for ImExam mode common atv_state common atv_images if (!d.name NE state.graphicsdevice) then return if (event.type EQ 0) then begin case event.press of 1: atv_apphot 2: atv_zoom, 'none', /recenter 4: atv_showstats else: endcase endif if (event.type EQ 2) then atv_draw_motion_event, event widget_control, state.draw_widget_id, /sensitive, /input_focus end ;-------------------------------------------------------------------- pro atv_draw_motion_event, event ; Event handler for motion events in draw window common atv_state if (!d.name NE state.graphicsdevice) then return tmp_event = [event.x, event.y] state.coord = $ round( (0.5 > ((tmp_event / state.zoom_factor) + state.offset) $ < (state.image_size - 0.5) ) - 0.5) atv_gettrack widget_control, state.draw_widget_id, /sensitive, /input_focus ;if atv_pixtable on, then create a 5x5 array of pixel values and the ;X & Y location strings that are fed to the pixel table if (xregistered('atv_pixtable', /noshow)) then atv_pixtable_update end ;---------------------------------------------------------------------- pro atv_draw_vector_event, event ; Check for left button press/depress, then get coords at point 1 and ; point 2. Call atv_lineplot. Calculate vector distance between ; endpoints and plot Vector Distance vs. Pixel Value with atv_vectorplot common atv_state common atv_images if (!d.name NE state.graphicsdevice) then return ;atv_setwindow, state.draw_window_id case event.type of 0: begin ; button press if (event.press EQ 1) then begin ; left button press state.vector_coord1[0] = state.coord[0] state.vector_coord1[1] = state.coord[1] state.vectorstart = [event.x, event.y] atv_drawvector, event state.vectorpress = 1 endif end 1: begin ; button release if (event.release EQ 1) then begin ; left button release state.vectorpress = 0 state.vector_coord2[0] = state.coord[0] state.vector_coord2[1] = state.coord[1] atv_drawvector, event atv_vectorplot, /newcoord endif end 2: begin ; motion event atv_draw_motion_event, event if (state.vectorpress EQ 1) then atv_drawvector, event end else: endcase widget_control, state.draw_widget_id, /sensitive, /input_focus end ;---------------------------------------------------------------------- pro atv_drawvector, event common atv_state ; button press: create initial pixmap and start drawing vector if (event.type EQ 0) then begin window, /free, xsize = state.draw_window_size[0], $ ysize = state.draw_window_size[1], /pixmap state.vector_pixmap_id = !d.window device, copy=[0, 0, state.draw_window_size[0], $ state.draw_window_size[1], 0, 0, state.draw_window_id] atv_resetwindow endif ; button release: redisplay initial image if (event.type EQ 1) then begin atv_setwindow, state.draw_window_id device, copy=[0, 0, state.draw_window_size[0], $ state.draw_window_size[1], 0, 0, state.vector_pixmap_id] atv_resetwindow wdelete, state.vector_pixmap_id endif ; motion event: redraw with new vector if (event.type EQ 2) then begin atv_setwindow, state.draw_window_id device, copy=[0, 0, state.draw_window_size[0], $ state.draw_window_size[1], 0, 0, state.vector_pixmap_id] xvector = [state.vectorstart[0], event.x] yvector = [state.vectorstart[1], event.y] plots, xvector, yvector, /device, color = state.box_color atv_resetwindow endif end ;---------------------------------------------------------------------- pro atv_pan_event, event ; event procedure for moving the box around in the pan window common atv_state if (!d.name NE state.graphicsdevice) then return case event.type of 0: begin ; button press widget_control, state.pan_widget_id, draw_motion_events = 1 atv_pantrack, event end 1: begin ; button release widget_control, state.pan_widget_id, draw_motion_events = 0 widget_control, state.pan_widget_id, /clear_events atv_pantrack, event atv_refresh end 2: begin atv_pantrack, event ; motion event widget_control, state.pan_widget_id, /clear_events end else: endcase end ;-------------------------------------------------------------------- pro atv_event, event ; Main event loop for atv top-level base, and for all the buttons. common atv_state common atv_images common atv_color common atv_pdata widget_control, event.id, get_uvalue = uvalue if (!d.name NE state.graphicsdevice and uvalue NE 'done') then return ; Get currently active window atv_getwindow case uvalue of 'atv_base': begin c = where(tag_names(event) EQ 'ENTER', count) if (count EQ 0) then begin ; resize event atv_resize atv_refresh endif end 'mode': case event.index of 0: state.mousemode = 'color' 1: state.mousemode = 'zoom' 2: state.mousemode = 'blink' 3: state.mousemode = 'imexam' 4: state.mousemode = 'vector' else: print, 'Unknown mouse mode!' endcase 'invert': begin ; invert the color table state.invert_colormap = abs(state.invert_colormap - 1) r_vector = reverse(r_vector) g_vector = reverse(g_vector) b_vector = reverse(b_vector) atv_setwindow, state.draw_window_id atv_stretchct atv_resetwindow ; For 24-bit color, need to refresh display after stretching color ; map. Can refresh in /fast mode if there are no overplots if (state.bitdepth EQ 24) then begin if ptr_valid(plot_ptr[1]) then begin atv_refresh endif else begin atv_refresh, /fast endelse endif end 'restretch_button': atv_restretch 'min_text': begin ; text entry in 'min = ' box atv_get_minmax, uvalue, event.value atv_displayall end 'max_text': begin ; text entry in 'max = ' box atv_get_minmax, uvalue, event.value atv_displayall end 'autoscale_button': begin ; autoscale the image atv_autoscale atv_displayall end 'full_range': begin ; display the full intensity range state.min_value = state.image_min state.max_value = state.image_max if state.min_value GE state.max_value then begin state.min_value = state.max_value - 1 state.max_value = state.max_value + 1 endif atv_set_minmax atv_displayall end 'zoom_in': atv_zoom, 'in' ; zoom buttons 'zoom_out': atv_zoom, 'out' 'zoom_one': atv_zoom, 'one' 'center': begin ; center image and preserve current zoom level state.centerpix = round(state.image_size / 2.) atv_refresh end 'fullview': atv_fullview 'done': if (state.activator EQ 0) then atv_shutdown $ else state.activator = 0 else: print, 'No match for uvalue....' ; bad news if this happens endcase end ;---------------------------------------------------------------------- pro atv_message, msg_txt, msgtype=msgtype, window=window ; Routine to display an error or warning message. Message can be ; displayed either to the IDL command line or to a popup window, ; depending on whether /window is set. ; msgtype must be 'warning', 'error', or 'information'. common atv_state if (n_elements(window) EQ 0) then window = 0 if (window EQ 1) then begin ; print message to popup window case msgtype of 'warning': t = dialog_message(msg_txt, dialog_parent = state.base_id) 'error': t = $ dialog_message(msg_txt,/error,dialog_parent=state.base_id) 'information': t = $ dialog_message(msg_txt,/information,dialog_parent=state.base_id) else: endcase endif else begin ; print message to IDL console message = strcompress(strupcase(msgtype) + ': ' + msg_txt) print, message endelse end ;----------------------------------------------------------------------- ; main atv routines for scaling, displaying, cursor tracking... ;----------------------------------------------------------------------- pro atv_displayall ; Call the routines to scale the image, make the pan image, and ; re-display everything. Use this if the scaling changes (log/ ; linear/ histeq), or if min or max are changed, or if a new image is ; passed to atv. If the display image has just been moved around or ; zoomed without a change in scaling, then just call atv_refresh ; rather than this routine. atv_scaleimage atv_makepan atv_refresh end ;--------------------------------------------------------------------- pro atv_refresh, fast = fast ; Make the display image from the scaled_image, and redisplay the pan ; image and tracking image. ; The /fast option skips the steps where the display_image is ; recalculated from the main_image. The /fast option is used in 24 ; bit color mode, when the color map has been stretched but everything ; else stays the same. common atv_state common atv_images atv_getwindow if (not(keyword_set(fast))) then begin atv_getoffset atv_getdisplay atv_displaymain atv_plotall endif else begin atv_displaymain endelse ; redisplay the pan image and plot the boundary box atv_setwindow, state.pan_pixmap erase tv, pan_image, state.pan_offset[0], state.pan_offset[1] atv_resetwindow atv_setwindow, state.pan_window_id if (not(keyword_set(fast))) then erase tv, pan_image, state.pan_offset[0], state.pan_offset[1] atv_resetwindow atv_drawbox, /norefresh if (state.bitdepth EQ 24) then atv_colorbar ; redisplay the tracking image if (not(keyword_set(fast))) then atv_gettrack atv_resetwindow state.newrefresh = 1 end ;-------------------------------------------------------------------- pro atv_getdisplay ; make the display image from the scaled image by applying the zoom ; factor and matching to the size of the draw window, and display the ; image. common atv_state common atv_images widget_control, /hourglass display_image = bytarr(state.draw_window_size[0], state.draw_window_size[1]) view_min = round(state.centerpix - $ (0.5 * state.draw_window_size / state.zoom_factor)) view_max = round(view_min + state.draw_window_size / state.zoom_factor) view_min = (0 > view_min < (state.image_size - 1)) view_max = (0 > view_max < (state.image_size - 1)) newsize = round( (view_max - view_min + 1) * state.zoom_factor) > 1 startpos = abs( round(state.offset * state.zoom_factor) < 0) ; Use interp & center keywords to congrid for zoomfactor < 1 : ; improvement contributed by N. Cunningham, added 4/14/06 if (state.zoom_factor LT 1.0) then begin tmp_image = congrid(scaled_image[view_min[0]:view_max[0], $ view_min[1]:view_max[1]], $ newsize[0], newsize[1], /center, /interp) endif else begin tmp_image = congrid(scaled_image[view_min[0]:view_max[0], $ view_min[1]:view_max[1]], $ newsize[0], newsize[1]) endelse xmax = newsize[0] < (state.draw_window_size[0] - startpos[0]) ymax = newsize[1] < (state.draw_window_size[1] - startpos[1]) display_image[startpos[0], startpos[1]] = tmp_image[0:xmax-1, 0:ymax-1] tmp_image = 0 end ;----------------------------------------------------------------------- pro atv_displaymain ; Display the main image and overplots common atv_state common atv_images atv_setwindow, state.draw_window_id tv, display_image atv_resetwindow end ;-------------------------------------------------------------------- pro atv_getoffset common atv_state ; Routine to calculate the display offset for the current value of ; state.centerpix, which is the central pixel in the display window. state.offset = $ round( state.centerpix - $ (0.5 * state.draw_window_size / state.zoom_factor) ) end ;---------------------------------------------------------------------- pro atv_makepan ; Make the 'pan' image that shows a miniature version of the full image. common atv_state common atv_images sizeratio = state.image_size[1] / state.image_size[0] if (sizeratio GE 1) then begin state.pan_scale = float(state.pan_window_size) / float(state.image_size[1]) endif else begin state.pan_scale = float(state.pan_window_size) / float(state.image_size[0]) endelse tmp_image = $ scaled_image[0:state.image_size[0]-1, 0:state.image_size[1]-1] pan_image = $ congrid(tmp_image, round(state.pan_scale * state.image_size[0])>1, $ round(state.pan_scale * state.image_size[1])>1, $ /center, /interp) state.pan_offset[0] = round((state.pan_window_size - (size(pan_image))[1]) / 2) state.pan_offset[1] = round((state.pan_window_size - (size(pan_image))[2]) / 2) end ;---------------------------------------------------------------------- pro atv_move_cursor, direction ; Use keypad arrow keys to step cursor one pixel at a time. ; Get the new track image, and update the cursor position. common atv_state i = 1L case direction of '2': state.coord[1] = max([state.coord[1] - i, 0]) '4': state.coord[0] = max([state.coord[0] - i, 0]) '8': state.coord[1] = min([state.coord[1] + i, state.image_size[1] - i]) '6': state.coord[0] = min([state.coord[0] + i, state.image_size[0] - i]) '7': begin state.coord[1] = min([state.coord[1] + i, state.image_size[1] - i]) state.coord[0] = max([state.coord[0] - i, 0]) end '9': begin state.coord[1] = min([state.coord[1] + i, state.image_size[1] - i]) state.coord[0] = min([state.coord[0] + i, state.image_size[0] - i]) end '3': begin state.coord[1] = max([state.coord[1] - i, 0]) state.coord[0] = min([state.coord[0] + i, state.image_size[0] - i]) end '1': begin state.coord[1] = max([state.coord[1] - i, 0]) state.coord[0] = max([state.coord[0] - i, 0]) end endcase newpos = (state.coord - state.offset + 0.5) * state.zoom_factor atv_setwindow, state.draw_window_id tvcrs, newpos[0], newpos[1], /device atv_resetwindow atv_gettrack ; If pixel table widget is open, update pixel values and cursor position if (xregistered('atv_pixtable', /noshow)) then atv_pixtable_update ; Prevent the cursor move from causing a mouse event in the draw window widget_control, state.draw_widget_id, /clear_events atv_resetwindow end ;---------------------------------------------------------------------- pro atv_set_minmax ; Updates the min and max text boxes with new values. common atv_state widget_control, state.min_text_id, set_value = string(state.min_value) widget_control, state.max_text_id, set_value = string(state.max_value) end ;---------------------------------------------------------------------- pro atv_get_minmax, uvalue, newvalue ; Change the min and max state variables when user inputs new numbers ; in the text boxes. common atv_state case uvalue of 'min_text': begin if (newvalue LT state.max_value) then begin state.min_value = newvalue endif end 'max_text': begin if (newvalue GT state.min_value) then begin state.max_value = newvalue endif end endcase atv_set_minmax end ;-------------------------------------------------------------------- pro atv_zoom, zchange, recenter = recenter common atv_state ; Routine to do zoom in/out and recentering of image. The /recenter ; option sets the new display center to the current cursor position. case zchange of 'in': state.zoom_level = (state.zoom_level + 1) < 6 'out': begin sizeratio = fix(min(state.image_size) / 16.) > 1 minzoom = -1.*fix(alog(sizeratio)/alog(2.0)) state.zoom_level = (state.zoom_level - 1) > minzoom end 'onesixteenth': state.zoom_level = -4 'oneeighth': state.zoom_level = -3 'onefourth': state.zoom_level = -2 'onehalf': state.zoom_level = -1 'two': state.zoom_level = 1 'four': state.zoom_level = 2 'eight': state.zoom_level = 3 'sixteen': state.zoom_level = 4 'one': state.zoom_level = 0 'none': ; no change to zoom level: recenter on current mouse position else: print, 'problem in atv_zoom!' endcase state.zoom_factor = (2.0)^(state.zoom_level) if (n_elements(recenter) GT 0) then begin state.centerpix = state.coord atv_getoffset endif atv_refresh if (n_elements(recenter) GT 0) then begin newpos = (state.coord - state.offset + 0.5) * state.zoom_factor atv_setwindow, state.draw_window_id tvcrs, newpos[0], newpos[1], /device atv_resetwindow atv_gettrack endif atv_resetwindow end ;----------------------------------------------------------------------- pro atv_fullview common atv_state ; set the zoom level so that the full image fits in the display window sizeratio = float(state.image_size) / float(state.draw_window_size) maxratio = (max(sizeratio)) state.zoom_level = floor((alog(maxratio) / alog(2.0)) * (-1)) state.zoom_factor = (2.0)^(state.zoom_level) ; recenter state.centerpix = round(state.image_size / 2.) atv_refresh atv_resetwindow end ;---------------------------------------------------------------------- pro atv_invert, ichange common atv_state common atv_images ; Routine to do image axis-inversion (X,Y,X&Y) case ichange of 'x': begin if ptr_valid(state.astr_ptr) then begin hreverse, main_image, *(state.head_ptr), $ main_image, *(state.head_ptr), 1, /silent head = *(state.head_ptr) atv_setheader, head endif else begin main_image = reverse(main_image,1) endelse end 'y': begin if ptr_valid(state.astr_ptr) then begin hreverse, main_image, *(state.head_ptr), $ main_image, *(state.head_ptr), 2, /silent head = *(state.head_ptr) atv_setheader, head endif else begin main_image = reverse(main_image,2) endelse end 'xy': begin if ptr_valid(state.astr_ptr) then begin hreverse, main_image, *(state.head_ptr), $ main_image, *(state.head_ptr), 1, /silent hreverse, main_image, *(state.head_ptr), $ main_image, *(state.head_ptr), 2, /silent head = *(state.head_ptr) atv_setheader, head endif else begin main_image = reverse(main_image,1) main_image = reverse(main_image,2) endelse end else: print, 'problem in atv_invert!' endcase atv_getstats, /align, /noerase ;Redisplay inverted image with current zoom, update pan, and refresh image atv_displayall ;make sure that the image arrays are updated for line/column plots, etc. atv_resetwindow end ;------------------------------------------------------------------ pro atv_rotate, rchange, get_angle=get_angle common atv_state common atv_images ; Routine to do image rotation ; If /get_angle set, create widget to enter rotation angle widget_control, /hourglass if (keyword_set(get_angle)) then begin formdesc = ['0, float,, label_left=Rotation Angle: ', $ '1, base, , row', $ '0, button, Cancel, quit', $ '0, button, Rotate, quit'] textform = cw_form(formdesc, /column, title = 'Rotate') if (textform.tag2 EQ 1) then return if (textform.tag3 EQ 1) then rchange = textform.tag0 endif if (not(keyword_set(get_angle)) OR (rchange EQ '90') or $ (rchange EQ '180') OR (rchange EQ '270') $ OR (rchange EQ '0')) then begin case rchange of '0': ; do nothing '90': begin if ptr_valid(state.astr_ptr) then begin hrotate, main_image, *(state.head_ptr), $ main_image, *(state.head_ptr), 3 endif else begin main_image = rotate(main_image, 3) endelse end '180': begin if ptr_valid(state.astr_ptr) then begin hrotate, main_image, *(state.head_ptr), $ main_image, *(state.head_ptr), 2 endif else begin main_image = rotate(main_image, 2) endelse end '270': begin if ptr_valid(state.astr_ptr) then begin hrotate, main_image, *(state.head_ptr), $ main_image, *(state.head_ptr), 1 endif else begin main_image = rotate(main_image, 1) endelse end endcase endif else begin ; arbitrary rotation angle rchange = float(rchange) if ptr_valid(state.astr_ptr) then begin hrot, main_image, *(state.head_ptr), $ main_image, *(state.head_ptr), rchange, -1, -1, 2, $ cubic=-0.5, missing=0 endif else begin main_image = rot(main_image, rchange, $ cubic=-0.5, missing=0) endelse endelse ;Update header information after rotation if header is present if ptr_valid(state.head_ptr) then begin head = *(state.head_ptr) atv_setheader, head endif atv_getstats, /align, /noerase ;Redisplay image with current zoom, update pan, and refresh image atv_displayall ;make sure that the image arrays are updated for line/column plots, etc. atv_resetwindow end ;------------------------------------------------------------------ pro atv_autoscale ; Routine to auto-scale the image. common atv_state common atv_images widget_control, /hourglass state.min_value = state.skymode - (2.0*state.skysig) > state.image_min if (state.scaling LE 1) then begin case state.scaling of 0: highval = 2.0 1: highval = 4.0 endcase state.max_value = state.skymode + (highval*stddev(main_image)) $ < state.image_max endif else begin state.max_value = state.image_max endelse if (finite(state.min_value) EQ 0) then state.min_value = state.image_min if (finite(state.max_value) EQ 0) then state.max_value = state.image_max if (state.min_value GE state.max_value) then begin state.min_value = state.min_value - 1 state.max_value = state.max_value + 1 endif atv_set_minmax end ;-------------------------------------------------------------------- pro atv_restretch ; Routine to restretch the min and max to preserve the display ; visually but use the full color map linearly. Written by DF, and ; tweaked and debugged by AJB. It doesn't always work exactly the way ; you expect (especially in log-scaling mode), but mostly it works fine. common atv_state sx = state.brightness sy = state.contrast if (state.scaling EQ 2) then return ; do nothing for hist-eq mode if (state.scaling EQ 0) then begin sfac = (state.max_value-state.min_value) state.max_value = sfac*(sx+sy)+state.min_value state.min_value = sfac*(sx-sy)+state.min_value endif if (state.scaling EQ 1) then begin offset = state.min_value - $ (state.max_value - state.min_value) * 0.01 sfac = alog10((state.max_value - offset) / (state.min_value - offset)) state.max_value = 10.^(sfac*(sx+sy)+alog10(state.min_value - offset)) $ + offset state.min_value = 10.^(sfac*(sx-sy)+alog10(state.min_value - offset)) $ + offset endif ; Try different behavior in asinh mode: usually want to keep the min ; value the same and just adjust the max value. Seems to work ok. if (state.scaling EQ 3) then begin sfac = asinh(state.max_value / state.asinh_beta) - $ asinh(state.min_value / state.asinh_beta) state.max_value = sinh(sfac*(sx+sy) + $ asinh(state.min_value/state.asinh_beta))*state.asinh_beta endif ; do this differently for 8 or 24 bit color, to prevent flashing atv_setwindow, state.draw_window_id if (state.bitdepth EQ 8) then begin atv_set_minmax atv_displayall state.brightness = 0.5 ; reset these state.contrast = 0.5 atv_stretchct endif else begin state.brightness = 0.5 ; reset these state.contrast = 0.5 atv_stretchct atv_set_minmax atv_displayall endelse atv_resetwindow end ;--------------------------------------------------------------------- function atv_wcsstring, lon, lat, ctype, equinox, disp_type, disp_equinox, $ disp_base60 common atv_state ; Routine to return a string which displays cursor coordinates. ; Allows choice of various coordinate systems. ; Contributed by D. Finkbeiner, April 2000. ; 29 Sep 2000 - added degree (RA,dec) option DPF ; Apr 2007: AJB added additional error checking to prevent crashes ; ctype - coord system in header ; disp_type - type of coords to display headtype = strmid(ctype[0], 0, 4) ; need numerical equinox values IF (equinox EQ 'J2000') THEN num_equinox = 2000.0 ELSE $ IF (equinox EQ 'B1950') THEN num_equinox = 1950.0 ELSE $ num_equinox = float(equinox) IF (disp_equinox EQ 'J2000') THEN num_disp_equinox = 2000.0 ELSE $ IF (disp_equinox EQ 'B1950') THEN num_disp_equinox = 1950.0 ELSE $ num_disp_equinox = float(equinox) ; first convert lon,lat to RA,dec (J2000) CASE headtype OF 'GLON': euler, lon, lat, ra, dec, 2 ; J2000 'ELON': BEGIN euler, lon, lat, ra, dec, 4 ; J2000 IF num_equinox NE 2000.0 THEN precess, ra, dec, num_equinox, 2000.0 END 'RA--': BEGIN ra = lon dec = lat IF num_equinox NE 2000.0 THEN precess, ra, dec, num_equinox, 2000.0 END 'DEC-': BEGIN ; for SDSS images ra = lon dec = lat IF num_equinox NE 2000.0 THEN precess, ra, dec, num_equinox, 2000.0 END else: begin wcsstring = '---No WCS Info---' widget_control, state.wcs_bar_id, set_value = wcsstring state.wcstype = 'none' return, wcsstring end ENDCASE ; Now convert RA,dec (J2000) to desired display coordinates: IF (disp_type[0] EQ 'RA--' or disp_type[0] EQ 'DEC-') THEN BEGIN ; generate (RA,dec) string disp_ra = ra disp_dec = dec IF num_disp_equinox NE 2000.0 THEN precess, disp_ra, disp_dec, $ 2000.0, num_disp_equinox IF disp_base60 THEN BEGIN ; (hh:mm:ss) format neg_dec = disp_dec LT 0 radec, disp_ra, abs(disp_dec), ihr, imin, xsec, ideg, imn, xsc wcsstring = string(ihr, imin, xsec, ideg, imn, xsc, disp_equinox, $ format = '(i2.2,":",i2.2,":",f6.3," ",i2.2,":",i2.2,":",f5.2," ",a6)' ) if (strmid(wcsstring, 6, 1) EQ ' ') then $ strput, wcsstring, '0', 6 if (strmid(wcsstring, 21, 1) EQ ' ') then $ strput, wcsstring, '0', 21 IF neg_dec THEN strput, wcsstring, '-', 14 ENDIF ELSE BEGIN ; decimal degree format wcsstring = string(disp_ra, disp_dec, disp_equinox, $ format='("Deg ",F9.5,",",F9.5,a6)') ENDELSE ENDIF IF disp_type[0] EQ 'GLON' THEN BEGIN ; generate (l,b) string euler, ra, dec, l, b, 1 wcsstring = string(l, b, format='("Galactic (",F9.5,",",F9.5,")")') ENDIF IF disp_type[0] EQ 'ELON' THEN BEGIN ; generate (l,b) string disp_ra = ra disp_dec = dec IF num_disp_equinox NE 2000.0 THEN precess, disp_ra, disp_dec, $ 2000.0, num_disp_equinox euler, disp_ra, disp_dec, lam, bet, 3 wcsstring = string(lam, bet, format='("Ecliptic (",F9.5,",",F9.5,")")') ENDIF return, wcsstring END ;---------------------------------------------------------------------- function atv_wavestring ; function to return string with wavelength info for spectral images. ; Currently works for HST STIS 2-d images. common atv_state cd = float(sxpar(*state.head_ptr,'CD1_1', /silent)) crpix = float(sxpar(*state.head_ptr,'CRPIX1', /silent)) crval = float(sxpar(*state.head_ptr,'CRVAL1', /silent)) shifta = float(sxpar(*state.head_ptr, 'SHIFTA1', /silent)) wavelength = crval + ((state.coord[0] - crpix) * cd) + (shifta * cd) wstring = string(wavelength, format='(F8.2)') wavestring = strcompress('Wavelength: ' + wstring + ' ' + state.cunit) return, wavestring end ;-------------------------------------------------------------------- pro atv_gettrack ; Create the image to display in the track window that tracks ; cursor movements. Also update the coordinate display and the ; (x,y) and pixel value. common atv_state common atv_images ; Get x and y for center of track window zcenter = (0 > state.coord < state.image_size) track = bytarr(11,11) boxsize=5 xmin = 0 > (zcenter[0] - boxsize) xmax = (zcenter[0] + boxsize) < (state.image_size[0] - 1) ymin = 0 > (zcenter[1] - boxsize) ymax = (zcenter[1] + boxsize) < (state.image_size[1] - 1) startx = abs( (zcenter[0] - boxsize) < 0 ) starty = abs( (zcenter[1] - boxsize) < 0 ) track[startx,starty] = scaled_image[xmin:xmax,ymin:ymax] track_image = rebin(track, $ state.track_window_size, state.track_window_size, $ /sample) atv_setwindow, state.track_window_id tv, track_image ; Overplot an X on the central pixel in the track window, to show the ; current mouse position ; Changed central x to be green always plots, [0.46, 0.54], [0.46, 0.54], /normal, color = state.box_color, psym=0 plots, [0.46, 0.54], [0.54, 0.46], /normal, color = state.box_color, psym=0 ; update location bar with x, y, and pixel value loc_string = $ string(state.coord[0], $ state.coord[1], $ main_image[state.coord[0], $ state.coord[1]], $ format = '("(",i5,",",i5,") ",g12.5)') widget_control, state.location_bar_id, set_value = loc_string ; Update coordinate display. if (state.wcstype EQ 'angle') then begin xy2ad, state.coord[0], state.coord[1], *(state.astr_ptr), lon, lat wcsstring = atv_wcsstring(lon, lat, (*state.astr_ptr).ctype, $ state.equinox, state.display_coord_sys, $ state.display_equinox, state.display_base60) widget_control, state.wcs_bar_id, set_value = wcsstring endif if (state.wcstype EQ 'lambda') then begin wavestring = atv_wavestring() widget_control, state.wcs_bar_id, set_value = wavestring endif atv_resetwindow end ;---------------------------------------------------------------------- pro atv_drawbox, norefresh=norefresh ; routine to draw the box on the pan window, given the current center ; of the display image. common atv_state common atv_images atv_setwindow, state.pan_window_id view_min = round(state.centerpix - $ (0.5 * state.draw_window_size / state.zoom_factor)) view_max = round(view_min + state.draw_window_size / state.zoom_factor) - 1 ; Create the vectors which contain the box coordinates box_x = float((([view_min[0], $ view_max[0], $ view_max[0], $ view_min[0], $ view_min[0]]) * state.pan_scale) + state.pan_offset[0]) box_y = float((([view_min[1], $ view_min[1], $ view_max[1], $ view_max[1], $ view_min[1]]) * state.pan_scale) + state.pan_offset[1]) ; set limits on box to make sure all sides always appear w = where(box_x LT 0, count) if (count GT 0) then box_x[w] = 0 w = where(box_y LT 0, count) if (count GT 0) then box_y[w] = 0 w = where(box_x GT state.pan_window_size-1, count) if (count GT 0) then box_x[w] = state.pan_window_size-1 w = where(box_y GT state.pan_window_size-1, count) if (count GT 0) then box_y[w] = state.pan_window_size-1 ; Redraw the pan image and overplot the box if (not(keyword_set(norefresh))) then $ device, copy=[0,0,state.pan_window_size, state.pan_window_size, 0, 0, $ state.pan_pixmap] plots, box_x, box_y, /device, color = state.box_color, psym=0 atv_resetwindow end ;---------------------------------------------------------------------- pro atv_pantrack, event ; routine to track the view box in the pan window during cursor motion common atv_state ; get the new box coords and draw the new box tmp_event = [event.x, event.y] newpos = state.pan_offset > tmp_event < $ (state.pan_offset + (state.image_size * state.pan_scale)) state.centerpix = round( (newpos - state.pan_offset ) / state.pan_scale) atv_drawbox atv_getoffset end ;---------------------------------------------------------------------- pro atv_resize ; Routine to resize the draw window when a top-level resize event ; occurs. common atv_state widget_control, state.base_id, tlb_get_size=tmp_event window = (state.base_min_size > tmp_event) newbase = window - state.base_pad newxsize = (tmp_event[0] - state.base_pad[0]) > $ (state.base_min_size[0] - state.base_pad[0]) newysize = (tmp_event[1] - state.base_pad[1]) > $ (state.base_min_size[1] - state.base_pad[1]) widget_control, state.draw_widget_id, $ scr_xsize = newxsize, scr_ysize = newysize widget_control, state.colorbar_widget_id, $ scr_xsize = newxsize, scr_ysize = state.colorbar_height state.draw_window_size = [newxsize, newysize] atv_colorbar widget_control, state.base_id, /clear_events widget_control, state.draw_base_id, /sensitive, /input_focus end ;---------------------------------------------------------------------- pro atv_scaleimage ; Create a byte-scaled copy of the image, scaled according to ; the state.scaling parameter. common atv_state common atv_images ; Since this can take some time for a big image, set the cursor ; to an hourglass until control returns to the event loop. widget_control, /hourglass scaled_image=0 case state.scaling of 0: scaled_image = $ ; linear stretch bytscl(main_image, $ /nan, $ min=state.min_value, $ max=state.max_value, $ top = state.ncolors - 1) + 8 1: begin ; log stretch offset = state.min_value - $ (state.max_value - state.min_value) * 0.01 scaled_image = $ bytscl( alog10(main_image - offset), $ min=alog10(state.min_value - offset), /nan, $ max=alog10(state.max_value - offset), $ top=state.ncolors - 1) + 8 end 2: scaled_image = $ ; histogram equalization bytscl(hist_equal(main_image, $ minv = state.min_value, $ maxv = state.max_value), $ /nan, top = state.ncolors - 1) + 8 3: begin scaled_image = bytscl(asinh((main_image - state.min_value) $ / state.asinh_beta), $ min = 0, $ max = asinh((state.max_value - state.min_value) / $ state.asinh_beta), $ /nan, top = state.ncolors - 1) + 8 end endcase end ;---------------------------------------------------------------------- pro atv_setasinh ; get the asinh beta parameter common atv_state b = string(state.asinh_beta) formline = strcompress('0,float,' + b + $ ',label_left=Asinh beta parameter: ,width=10') formdesc = [formline, $ '0, button, Set beta, quit', $ '0, button, Cancel, quit'] textform = cw_form(formdesc, ids=ids, /column, $ title = 'atv asinh stretch settings') if (textform.tag2 EQ 1) then return state.asinh_beta = float(textform.tag0) atv_displayall end ;---------------------------------------------------------------------- pro atv_getstats, align=align, noerase=noerase ; Get basic image stats: min and max, and size. ; set align keyword to preserve alignment of previous image common atv_state common atv_images ; this routine operates on main_image, which is in the ; atv_images common block widget_control, /hourglass oldimagesize = state.image_size state.image_size = [ (size(main_image))[1], (size(main_image))[2] ] if ((oldimagesize[0] NE state.image_size[0]) OR $ (oldimagesize[1] NE state.image_size[1])) then align = 0 state.image_min = min(main_image, max=maxx, /nan) state.image_max = maxx ; Get sky value for autoscaling and asinh stretch. Eliminate ; zero-valued and NaN pixels from sky calculation, i.e. for HST ACS ; drizzled images, WFPC2 mosaics, or Spitzer images. w = where(finite(main_image) AND (main_image NE 0.0), goodcount) if (goodcount GT 25) then begin sky, main_image[w], skymode, skysig, /silent endif else if (goodcount GT 5 AND goodcount LE 25) then begin skysig = stddev(main_image[w]) skymode = median(main_image[w]) endif else if (goodcount LE 5) then begin ; really pathological images skysig = 1. skymode = 0. endif ; error checking- in case sky.pro returns a zero or negative sigma if (skysig LE 0.0) then skysig = stddev(main_image) if (skysig LE 0.0) then skysig = 1.0 state.skymode = skymode state.skysig = skysig state.asinh_beta = state.skysig if (state.min_value GE state.max_value) then begin state.min_value = state.min_value - 1 state.max_value = state.max_value + 1 endif ; zero the current display position on the center of the image, ; unless user selected /align keyword state.coord = round(state.image_size / 2.) IF (NOT keyword_set(align) OR (state.firstimage EQ 1)) THEN $ state.centerpix = round(state.image_size / 2.) atv_getoffset ; Clear all plot annotations if (not(keyword_set(noerase))) then atverase, /norefresh end ;------------------------------------------------------------------- pro atv_setwindow, windowid ; replacement for wset. Reads the current active window first. ; This should be used when the currently active window is an external ; (i.e. non-atv) idl window. Use atv_setwindow to set the window to ; one of the atv window, then display something to that window, then ; use atv_resetwindow to set the current window back to the currently ; active external window. Make sure that device is not set to ; postscript, because if it is we can't display anything. common atv_state common atv_color state.active_window_pmulti = !p.multi !p.multi = 0 tvlct, user_r, user_g, user_b, /get ; regenerate atv color table atv_initcolors atv_stretchct if (!d.name NE 'PS') then begin state.active_window_id = !d.window wset, windowid endif ; use for debugging ; print, 'atv_setwindow', state.active_window_id end ;--------------------------------------------------------------------- pro atv_resetwindow ; reset to current active window common atv_state common atv_color ; The empty command used below is put there to make sure that all ; graphics to the previous atv window actually get displayed to screen ; before we wset to a different window. Without it, some line ; graphics would not actually appear on screen. ; Also reset to user's external color map and p.multi. ; use for debugging ; print, 'atv_resetwindow', state.active_window_id if (!d.name NE 'PS') then begin empty wset, state.active_window_id tvlct, user_r, user_g, user_b endif !p.multi = state.active_window_pmulti end ;------------------------------------------------------------------ pro atv_getwindow ; get currently active window id common atv_state common atv_color if (!d.name NE 'PS') then begin state.active_window_id = !d.window endif ; use for debugging ; print, 'atv_getwindow', state.active_window_id ; get current external window color table tvlct, user_r, user_g, user_b, /get end ;------------------------------------------------------------------- pro atv_pixtable ; Create a table widget that will show a 5x5 array of pixel values ; around the current cursor position if (not(xregistered('atv_pixtable', /noshow))) then begin common atv_state common atv_images state.pixtable_base_id = $ widget_base(/base_align_right, $ group_leader = state.base_id, $ /column, $ title = 'atv pixel table') state.pixtable_tbl_id = $ widget_table(state.pixtable_base_id, $ value=[0,0], xsize=5, ysize=5, row_labels='', $ column_labels='', alignment=2, /resizeable_columns, $ column_widths = 3, units=2) pixtable_done = widget_button(state.pixtable_base_id, $ value = 'Done', $ uvalue = 'pixtable_done') widget_control, state.pixtable_base_id, /realize xmanager, 'atv_pixtable', state.pixtable_base_id, /no_block endif end ;--------------------------------------------------------------------- pro atv_pixtable_event, event common atv_state widget_control, event.id, get_uvalue = uvalue case uvalue of 'pixtable_done': widget_control, event.top, /destroy else: endcase end ;-------------------------------------------------------------------- pro atv_pixtable_update common atv_state common atv_images zcenter = (0 > state.coord < state.image_size[0:1]) ; Check and adjust the zcenter if the cursor is near the edges of the image if (zcenter[0] le 2) then zcenter[0] = 2 if (zcenter[0] gt (state.image_size[0]-3)) then $ zcenter[0] = state.image_size[0] - 3 if (zcenter[1] le 2) then zcenter[1] = 2 if (zcenter[1] gt (state.image_size[1]-3)) then $ zcenter[1] = state.image_size[1] - 3 ;pix_values = dblarr(5,5) row_labels = strarr(5) column_labels = strarr(5) boxsize=2 xmin = 0 > (zcenter[0] - boxsize) xmax = (zcenter[0] + boxsize) < (state.image_size[0] - 1) ymin = 0 > (zcenter[1] - boxsize) ymax = (zcenter[1] + boxsize) < (state.image_size[1] - 1) row_labels = [strcompress(string(ymax),/remove_all), $ strcompress(string(ymin+3),/remove_all), $ strcompress(string(ymin+2),/remove_all), $ strcompress(string(ymin+1),/remove_all), $ strcompress(string(ymin),/remove_all)] column_labels = [strcompress(string(xmin),/remove_all), $ strcompress(string(xmin+1),/remove_all), $ strcompress(string(xmin+2),/remove_all), $ strcompress(string(xmin+3),/remove_all), $ strcompress(string(xmax),/remove_all)] pix_values = main_image[xmin:xmax, ymin:ymax] pix_values = reverse(pix_values, 2, /overwrite) widget_control, state.pixtable_tbl_id, set_value = pix_values, $ column_labels=column_labels, row_labels=row_labels ; highlight the current image cursor position in the table wx = where(long(column_labels) EQ state.coord[0], count) wy = where(long(row_labels) EQ state.coord[1], count) widget_control, state.pixtable_tbl_id, set_table_select = [wx,wy,wx,wy] end ;-------------------------------------------------------------------- ; Fits file reading routines ;-------------------------------------------------------------------- pro atv_readfits, fitsfilename=fitsfilename, newimage=newimage ; Read in a new image when user goes to the File->ReadFits menu. ; Do a reasonable amount of error-checking first, to prevent unwanted ; crashes. common atv_state common atv_images newimage = 0 cancelled = 0 if (n_elements(fitsfilename) EQ 0) then window = 1 else window = 0 ; Added list of filters for SDSS suffixes, etc. filterlist = ['*.fits;*.fit;*.fit.gz;*.fits.gz;*.FIT;*.FITS;*.ftz;*.FTZ;*.ccd'] ; If fitsfilename hasn't been passed to this routine, get filename ; from dialog_pickfile. if (n_elements(fitsfilename) EQ 0) then begin fitsfile = $ dialog_pickfile(filter = filterlist, $ group = state.base_id, $ /must_exist, $ /read, $ path = state.current_dir, $ get_path = tmp_dir, $ title = 'Select Fits Image') if (tmp_dir NE '') then state.current_dir = tmp_dir if (fitsfile EQ '') then return ; 'cancel' button returns empty string endif else begin fitsfile = fitsfilename endelse ; Get fits header so we know what kind of image this is. head = headfits(fitsfile) ; Check validity of fits file header if (n_elements(strcompress(head, /remove_all)) LT 2) then begin atv_message, 'File does not appear to be a valid FITS image!', $ window = window, msgtype = 'error' return endif if (!ERR EQ -1) then begin atv_message, $ 'Selected file does not appear to be a valid FITS image!', $ msgtype = 'error', window = window return endif ; Find out if this is a fits extension file, and how many extensions ; New: use fits_open rather than fits_info fits_open, fitsfile, fcb, message = message if (message NE '') then begin atv_message, message, msgtype='error', /window return end numext = fcb.nextend fits_close, fcb instrume = strcompress(string(sxpar(head, 'INSTRUME')), /remove_all) origin = strcompress(sxpar(head, 'ORIGIN'), /remove_all) naxis = sxpar(head, 'NAXIS') ; Make sure it's not a 1-d spectrum if (numext EQ 0 AND naxis LT 2) then begin atv_message, 'Selected file is not a 2-d FITS image!', $ window = window, msgtype = 'error' return endif state.title_extras = '' ; Now call the subroutine that knows how to read in this particular ; data format: if ((numext GT 0) AND (instrume NE 'WFPC2')) then begin atv_fitsext_read, fitsfile, numext, head, cancelled endif else if ((instrume EQ 'WFPC2') AND (naxis EQ 3)) then begin atv_wfpc2_read, fitsfile, head, cancelled endif else if ((naxis EQ 3) AND (origin EQ '2MASS')) then begin atv_2mass_read, fitsfile, head, cancelled endif else begin atv_plainfits_read, fitsfile, head, cancelled endelse if (cancelled EQ 1) then return ; Make sure it's a 2-d image if ( (size(main_image))[0] NE 2 ) then begin atv_message, 'Selected file is not a 2-D fits image!', $ msgtype = 'error', window = window main_image = fltarr(512, 512) newimage = 1 return endif widget_control, /hourglass state.imagename = fitsfile atv_setheader, head newimage = 1 end ;---------------------------------------------------------- ; Subroutines for reading specific data formats ;--------------------------------------------------------------- pro atv_fitsext_read, fitsfile, numext, head, cancelled ; Fits reader for fits extension files common atv_state common atv_images numlist = '' for i = 1, numext do begin numlist = strcompress(numlist + string(i) + '|', /remove_all) endfor numlist = strmid(numlist, 0, strlen(numlist)-1) droptext = strcompress('0, droplist, ' + numlist + $ ', label_left=Select Extension:, set_value=0') formdesc = ['0, button, Read Primary Image, quit', $ '0, label, OR:', $ droptext, $ '0, button, Read Fits Extension, quit', $ '0, button, Cancel, quit'] textform = cw_form(formdesc, /column, $ title = 'Fits Extension Selector') if (textform.tag4 EQ 1) then begin ; cancelled cancelled = 1 return endif if (textform.tag3 EQ 1) then begin ;extension selected extension = long(textform.tag2) + 1 endif else begin extension = 0 ; primary image selected endelse ; Make sure it's not a fits table: this would make mrdfits crash head = headfits(fitsfile, exten=extension) xten = strcompress(sxpar(head, 'XTENSION'), /remove_all) if (xten EQ 'BINTABLE') then begin atv_message, 'File appears to be a FITS table, not an image.', $ msgtype='error', /window cancelled = 1 return endif if (extension GE 1) then begin state.title_extras = strcompress('Extension ' + string(extension)) endif else begin state.title_extras = 'Primary Image' endelse ; Read in the image main_image=0 ; use fits_read so that extension headers will inherit primary header ; keywords. Needed for HST ACS images. fits_read, fitsfile, main_image, head, exten_no = extension end ;---------------------------------------------------------------- pro atv_plainfits_read, fitsfile, head, cancelled common atv_images ; Fits reader for plain fits files, no extensions. main_image=0 fits_read, fitsfile, main_image, head end ;------------------------------------------------------------------ pro atv_wfpc2_read, fitsfile, head, cancelled ; Fits reader for 4-panel HST WFPC2 images common atv_state common atv_images droptext = strcompress('0, droplist,PC|WF2|WF3|WF4|Mosaic,' + $ 'label_left = Select WFPC2 CCD:, set_value=0') formdesc = [droptext, $ '0, button, Read WFPC2 Image, quit', $ '0, button, Cancel, quit'] textform = cw_form(formdesc, /column, title = 'WFPC2 CCD Selector') if (textform.tag2 EQ 1) then begin ; cancelled cancelled = 1 return endif ccd = long(textform.tag0) + 1 widget_control, /hourglass if (ccd LE 4) then begin main_image=0 wfpc2_read, fitsfile, main_image, head, num_chip = ccd endif if (ccd EQ 5) then begin main_image=0 wfpc2_read, fitsfile, main_image, head, /batwing endif case ccd of 1: state.title_extras = 'PC1' 2: state.title_extras = 'WF2' 3: state.title_extras = 'WF3' 4: state.title_extras = 'WF4' 5: state.title_extras = 'Mosaic' else: state.title_extras = '' endcase end ;---------------------------------------------------------------------- pro atv_2mass_read, fitsfile, head, cancelled ; Fits reader for 3-plane 2MASS Extended Source J/H/Ks data cube. common atv_state common atv_images droptext = strcompress('0, droplist,J|H|Ks,' + $ 'label_left = Select 2MASS Band:, set_value=0') formdesc = [droptext, $ '0, button, Read 2MASS Image, quit', $ '0, button, Cancel, quit'] textform = cw_form(formdesc, /column, title = '2MASS Band Selector') if (textform.tag2 EQ 1) then begin ; cancelled cancelled = 1 return endif main_image=0 main_image = mrdfits(fitsfile, 0, head, /silent, /fscale) band = long(textform.tag0) main_image = main_image[*,*,band] ; fixed 11/28/2000 case textform.tag0 of 0: state.title_extras = 'J Band' 1: state.title_extras = 'H Band' 2: state.title_extras = 'Ks Band' else: state.title_extras = '' endcase ; fix ctype2 in header to prevent crashes when running xy2ad routine: if (strcompress(sxpar(head, 'CTYPE2'), /remove_all) EQ 'DEC---SIN') then $ sxaddpar, head, 'CTYPE2', 'DEC--SIN' end ;---------------------------------------------------------------------- pro atv_getdss common atv_state common atv_images formdesc = ['0, text, , label_left=Object Name: , width=15, tag=objname', $ '0, button, NED|SIMBAD, set_value=0, label_left=Object Lookup:, exclusive, tag=lookupsource', $ '0, label, Or enter J2000 Coordinates:, CENTER', $ '0, text, , label_left=RA (hh:mm:ss.ss): , width=15, tag=ra', $ '0, text, , label_left=Dec (+dd:mm:ss.ss): , width=15, tag=dec', $ '0, droplist, 1st Generation|2nd Generation Blue|2nd Generation Red|2nd Generation Near-IR, label_left=Band:, set_value=0,tag=band ', $ '0, float, 10.0, label_left=Image Size (arcmin; max=60): ,tag=imsize', $ '1, base, , row', $ '0, button, GetImage, tag=getimage, quit', $ '0, button, Cancel, tag=cancel, quit'] archiveform = cw_form(formdesc, /column, title = 'atv: Get DSS Image') if (archiveform.cancel EQ 1) then return if (archiveform.imsize LE 0.0 OR archiveform.imsize GT 60.0) then begin atv_message, 'Image size must be between 0 and 60 arcmin.', $ msgtype='error', /window return endif case archiveform.band of 0: band = '1' 1: band = '2b' 2: band = '2r' 3: band = '2i' else: print, 'error in atv_getdss!' endcase case archiveform.lookupsource of 0: ned = 1 1: ned = 0 ; simbad lookup endcase widget_control, /hourglass if (archiveform.objname NE '') then begin ; user entered object name querysimbad, archiveform.objname, ra, dec, found=found, ned=ned, $ errmsg=errmsg if (found EQ 0) then begin atv_message, errmsg, msgtype='error', /window return endif endif else begin ; user entered ra, dec rastring = archiveform.ra decstring = archiveform.dec atv_getradec, rastring, decstring, ra, dec endelse ; as of nov 2006, stsci server doesn't seem to recognize '2i' ; band in the way it used to. Use eso server for 2i. if (band NE '2i') then begin querydss, [ra, dec], tmpimg, tmphdr, imsize=archiveform.imsize, $ survey=band endif else begin querydss, [ra, dec], tmpimg, tmphdr, imsize=archiveform.imsize, $ survey=band, /eso endelse atv, temporary(tmpimg), header=temporary(tmphdr) end ;----------------------------------------------------------------- pro atv_getfirst common atv_state common atv_images formdesc = ['0, text, , label_left=Object Name: , width=15, tag=objname', $ '0, button, NED|SIMBAD, set_value=0, label_left=Object Lookup:, exclusive, tag=lookupsource', $ '0, label, Or enter J2000 Coordinates:, CENTER', $ '0, text, , label_left=RA (hh:mm:ss.ss): , width=15, tag=ra', $ '0, text, , label_left=Dec (+dd:mm:ss.ss): , width=15, tag=dec', $ '0, float, 10.0, label_left=Image Size (arcmin; max=30): ,tag=imsize', $ '1, base, , row', $ '0, button, GetImage, tag=getimage, quit', $ '0, button, Cancel, tag=cancel, quit'] archiveform = cw_form(formdesc, /column, title = 'atv: Get FIRST Image') if (archiveform.cancel EQ 1) then return if (archiveform.imsize LE 0.0 OR archiveform.imsize GT 30.0) then begin atv_message, 'Image size must be between 0 and 30 arcmin.', $ msgtype='error', /window return endif imsize = string(round(archiveform.imsize)) case archiveform.lookupsource of 0: ned = 1 1: ned = 0 ; simbad lookup endcase widget_control, /hourglass if (archiveform.objname NE '') then begin ; user entered object name querysimbad, archiveform.objname, ra, dec, found=found, ned=ned, $ errmsg=errmsg if (found EQ 0) then begin atv_message, errmsg, msgtype='error', /window return endif ; convert decimal ra, dec to hms, dms sra = sixty(ra/15.0) rahour = string(round(sra[0])) ramin = string(round(sra[1])) rasec = string(sra[2]) if (dec LT 0) then begin decsign = '-' endif else begin decsign = '+' endelse sdec = sixty(abs(dec)) decdeg = strcompress(decsign + string(round(sdec[0])), /remove_all) decmin = string(round(sdec[1])) decsec = string(sdec[2]) endif else begin ; user entered ra, dec rastring = archiveform.ra decstring = archiveform.dec rtmp = rastring pos = strpos(rtmp, ':') if (pos EQ -1) then pos = strlen(rtmp) rahour = strmid(rtmp, 0, pos) rtmp = strmid(rtmp, pos+1) pos = strpos(rtmp, ':') if (pos EQ -1) then pos = strlen(rtmp) ramin = strmid(rtmp, 0, pos) rtmp = strmid(rtmp, pos+1) rasec = rtmp dtmp = decstring pos = strpos(dtmp, ':') if (pos EQ -1) then pos = strlen(dtmp) decdeg = strmid(dtmp, 0, pos) dtmp = strmid(dtmp, pos+1) pos = strpos(dtmp, ':') if (pos EQ -1) then pos = strlen(dtmp) decmin = strmid(dtmp, 0, pos) dtmp = strmid(dtmp, pos+1) decsec = dtmp endelse ; build the url to get image url = 'http://third.ucllnl.org/cgi-bin/firstimage' url = strcompress(url + '?RA=' + rahour + '%20' + ramin + '%20' + rasec, $ /remove_all) url = strcompress(url + '&Dec=' + decdeg + '%20' + decmin + '%20' + $ decsec, /remove_all) url = strcompress(url + '&Equinox=J2000&ImageSize=' + imsize + $ '&MaxInt=10&FITS=1&Download=1', /remove_all) ; now use webget to get the image result = webget(url) if (n_elements(result.image) LE 1) then begin atv_message, result.text, msgtype='error', /window return endif else begin ; valid image atv, result.image, header=result.imageheader result.header = '' result.text = '' result.imageheader = '' result.image = '' endelse end ;----------------------------------------------------------------- pro atv_getradec, rastring, decstring, ra, dec ; converts ra and dec strings in hh:mm:ss and dd:mm:ss to decimal degrees rtmp = rastring pos = strpos(rtmp, ':') if (pos EQ -1) then pos = strlen(rtmp) rahour = strmid(rtmp, 0, pos) rtmp = strmid(rtmp, pos+1) pos = strpos(rtmp, ':') if (pos EQ -1) then pos = strlen(rtmp) ramin = strmid(rtmp, 0, pos) rtmp = strmid(rtmp, pos+1) rasec = rtmp dtmp = decstring pos = strpos(dtmp, ':') if (pos EQ -1) then pos = strlen(dtmp) decdeg = strmid(dtmp, 0, pos) dtmp = strmid(dtmp, pos+1) pos = strpos(dtmp, ':') if (pos EQ -1) then pos = strlen(dtmp) decmin = strmid(dtmp, 0, pos) dtmp = strmid(dtmp, pos+1) decsec = dtmp ra = 15.0 * ten([rahour, ramin, rasec]) dec = ten([decdeg, decmin, decsec]) end ;----------------------------------------------------------------------- ; Routines for creating output graphics ;---------------------------------------------------------------------- pro atv_writefits ; Writes image to a FITS file common atv_state common atv_images ; Get filename to save image filename = dialog_pickfile(filter = '*.fits', $ file = 'atv.fits', $ dialog_parent = state.base_id, $ path = state.current_dir, $ get_path = tmp_dir, $ /write) if (tmp_dir NE '') then state.current_dir = tmp_dir if (strcompress(filename, /remove_all) EQ '') then return ; cancel if (filename EQ state.current_dir) then begin atv_message, 'Must indicate filename to save.', msgtype = 'error', /window return endif tmp_result = findfile(filename, count = nfiles) result = '' if (nfiles GT 0) then begin mesg = strarr(2) mesg[0] = 'Overwrite existing file:' mesg[1] = strcompress(filename + '?', /remove_all) result = dialog_message(mesg, $ /default_no, $ dialog_parent = state.base_id, $ /question) endif if (strupcase(result) EQ 'NO') then return if (ptr_valid(state.head_ptr)) then begin writefits, filename, main_image, (*state.head_ptr) endif else begin writefits, filename, main_image endelse end ;----------------------------------------------------------------------- pro atv_writeimage, imgtype common atv_state common atv_images tmpfilename = strcompress('atv.' + strlowcase(imgtype), /remove_all) filename = dialog_pickfile(file = tmpfilename, $ dialog_parent = state.base_id, $ path = state.current_dir, $ get_path = tmp_dir, $ /write) if (tmp_dir NE '') then state.current_dir = tmp_dir if (strcompress(filename, /remove_all) EQ '') then return ; cancel if (filename EQ state.current_dir) then begin atv_message, 'Must indicate filename to save.', msgtype = 'error', /window return endif ; check for pre-existing file tmp_result = findfile(filename, count = nfiles) result = '' if (nfiles GT 0) then begin mesg = strarr(2) mesg[0] = 'Overwrite existing file:' mesg[1] = strcompress(filename + '?', /remove_all) result = dialog_message(mesg, $ /default_no, $ dialog_parent = state.base_id, $ /question) endif if (strupcase(result) EQ 'NO') then return ; From here down this routine is based on Liam E. Gumley's SAVEIMAGE ; program, modified for use with ATV. quality = 75 ; for jpeg output ;- Check for TVRD capable device if ((!d.flags and 128)) eq 0 then begin atv_message, 'Unsupported graphics device- cannot create image.', $ msgtype='error', /window return endif depth = state.bitdepth ;- Handle window devices (other than the Z buffer) if (!d.flags and 256) ne 0 then begin ;- Copy the contents of the current display to a pixmap current_window = state.draw_window_id xsize = state.draw_window_size[0] ysize = state.draw_window_size[1] window, /free, /pixmap, xsize=xsize, ysize=ysize, retain=2 device, copy=[0, 0, xsize, ysize, 0, 0, current_window] ;- Set decomposed color mode for 24-bit displays if (depth gt 8) then device, get_decomposed=entry_decomposed device, decomposed=1 endif ;- Read the pixmap contents into an array if (depth gt 8) then begin image = tvrd(order=0, true=1) endif else begin image = tvrd(order=0) endelse ;- Handle window devices (other than the Z buffer) if (!d.flags and 256) ne 0 then begin ;- Restore decomposed color mode for 24-bit displays if (depth gt 8) then begin device, decomposed=entry_decomposed endif ;- Delete the pixmap wdelete, !d.window wset, current_window endif ;- Get the current color table tvlct, r, g, b, /get ;- If an 8-bit image was read, reduce the number of colors if (depth le 8) then begin reduce_colors, image, index r = r[index] g = g[index] b = b[index] endif ; write output file if (imgtype eq 'png') then begin write_png, filename, image, r, g, b endif if (imgtype eq 'jpg') or (imgtype eq 'tiff') then begin ;- Convert 8-bit image to 24-bit if (depth le 8) then begin info = size(image) nx = info[1] ny = info[2] true = bytarr(3, nx, ny) true[0, *, *] = r[image] true[1, *, *] = g[image] true[2, *, *] = b[image] image = temporary(true) endif ;- If TIFF format output, reverse image top to bottom if (imgtype eq 'tiff') then image = reverse(temporary(image), 3) ;- Write the image case imgtype of 'jpg' : write_jpeg, filename, image, true=1, quality=quality 'tiff' : write_tiff, filename, image, 1 else : endcase endif atv_resetwindow end ;---------------------------------------------------------------------- pro atv_makergb ; Makes an RGB truecolor png image from the 3 blink channels. ; Can be saved using file->writeimage. ; Note- untested for 8-bit displays. May not work there. common atv_state common atv_images if (n_elements(blink_image1) EQ 1 OR $ n_elements(blink_image2) EQ 1 OR $ n_elements(blink_image3) EQ 1) then begin atv_message, $ 'You need to set the 3 blink channels first to make an RGB image.', $ msgtype = 'error', /window return endif atv_getwindow window, /free, xsize = state.draw_window_size[0], $ ysize = state.draw_window_size[1], /pixmap tempwindow = !d.window tv, blink_image1, /true rimage = tvrd() tv, blink_image2, /true gimage = tvrd() tv, blink_image3, /true bimage = tvrd() tcimage = [[[rimage]], [[gimage]], [[bimage]]] tv, tcimage, true=3 tvlct, rmap, gmap, bmap, /get image = tvrd(/true) wdelete, tempwindow atv_setwindow, state.draw_window_id tv, image, /true atv_resetwindow end ;---------------------------------------------------------------------- pro atv_writeps ; Writes an encapsulated postscript file of the current display. ; Calls cmps_form to get postscript file parameters. ; Note. cmps_form blocks the command line but doesn't block atv ; menus. If we have one cmps_form active and invoke another one, it ; would crash. Use state.ispsformon to keep track of whether we have ; one active already or not. common atv_state common atv_images common atv_color if (state.ispsformon EQ 1) then return ; cmps_form.pro crashes if atv is in blocking mode. if (state.block EQ 1) then begin atv_message, 'PS output is disabled in blocking mode.', $ msgtype = 'warning', /window return endif widget_control, /hourglass view_min = round(state.centerpix - $ (0.5 * state.draw_window_size / state.zoom_factor)) ; bug fix from N. Cunningham here- modified 4/14/06 to fix centering ; of overplots on the image by subtracting 1 from the max size view_max = round(view_min + state.draw_window_size $ / state.zoom_factor - 1) xsize = (state.draw_window_size[0] / state.zoom_factor) > $ (view_max[0] - view_min[0] + 1) ysize = (state.draw_window_size[1] / state.zoom_factor) > $ (view_max[1] - view_min[1] + 1) aspect = float(ysize) / float(xsize) fname = strcompress(state.current_dir + 'atv.ps', /remove_all) atv_setwindow, state.draw_window_id tvlct, rr, gg, bb, 8, /get atv_resetwindow ; make sure that we don't keep the cmps_form window as the active window external_window_id = !d.window state.ispsformon = 1 psforminfo = cmps_form(cancel = canceled, create = create, $ aspect = aspect, parent = state.base_id, $ /preserve_aspect, $ xsize = 6.0, ysize = 6.0 * aspect, $ /color, /encapsulated, $ /nocommon, papersize='Letter', $ bits_per_pixel=8, $ filename = fname, $ button_names = ['Create PS File']) atv_setwindow, external_window_id state.ispsformon = 0 if (canceled) then return if (psforminfo.filename EQ '') then return tvlct, rr, gg, bb, 8 tmp_result = findfile(psforminfo.filename, count = nfiles) result = '' if (nfiles GT 0) then begin mesg = strarr(2) mesg[0] = 'Overwrite existing file:' tmp_string = $ strmid(psforminfo.filename, $ strpos(psforminfo.filename, state.delimiter, /reverse_search) + 1) mesg[1] = strcompress(tmp_string + '?', /remove_all) result = dialog_message(mesg, $ /default_no, $ dialog_parent = state.base_id, $ /question) endif if (strupcase(result) EQ 'NO') then return widget_control, /hourglass screen_device = !d.name ; In 8-bit mode, the screen color table will have fewer than 256 ; colors. Stretch out the existing color table to 256 colors for the ; postscript plot. set_plot, 'ps' device, _extra = psforminfo tvlct, rr, gg, bb, 8, /get rn = congrid(rr, 248) gn = congrid(gg, 248) bn = congrid(bb, 248) tvlct, temporary(rn), temporary(gn), temporary(bn), 8 ; Make a full-resolution version of the display image, accounting for ; scalable pixels in the postscript output newdisplay = bytarr(xsize, ysize) startpos = abs(round(state.offset) < 0) view_min = (0 > view_min < (state.image_size - 1)) view_max = (0 > view_max < (state.image_size - 1)) dimage = bytscl(scaled_image[view_min[0]:view_max[0], $ view_min[1]:view_max[1]], $ top = 247, min=8, max=(!d.table_size-1)) + 8 newdisplay[startpos[0], startpos[1]] = temporary(dimage) ; if there's blank space around the image border, keep it black tv, newdisplay atv_plotall if (state.frame EQ 1) then begin ; put frame around image plot, [0], [0], /nodata, position=[0,0,1,1], $ xrange=[0,1], yrange=[0,1], xstyle=5, ystyle=5, /noerase boxx = [0,0,1,1,0,0] boxy = [0,1,1,0,0,1] oplot, boxx, boxy, color=0, thick=state.framethick endif tvlct, temporary(rr), temporary(gg), temporary(bb), 8 device, /close set_plot, screen_device end ;---------------------------------------------------------------------- ; routines for defining the color maps ;---------------------------------------------------------------------- pro atv_stretchct, brightness, contrast, getcursor = getcursor ; routine to change color stretch for given values of brightness and contrast. ; Complete rewrite 2000-Sep-21 - Doug Finkbeiner ; Updated 12/2006 to allow for brightness,contrast param input ; without changing the state.brightness and state.contrast values. ; Better for surface plots in plot window. common atv_state common atv_color ; if GETCURSOR then assume mouse position passed and save as ; state.brightness and state.contrast. If no params passed, then use ; the current state.brightness and state.contrast. If b, c passed ; without /getcursor, then make a new color table stretch for that ; brightness and contrast but don't modify the current ; state.brightness and state.contrast ; New in 2.0: scale the contrast by 0.75- gives better contrast by ; default when first starting up, and better in general with asinh ; scaling contrastscale=0.75 if (keyword_set(getcursor)) then begin state.brightness = brightness/float(state.draw_window_size[0]) state.contrast = contrast/float(state.draw_window_size[1]) x = state.brightness*(state.ncolors-1) y = state.contrast*(state.ncolors-1)*contrastscale > 2 endif else begin if (n_elements(brightness) EQ 0 OR n_elements(contrast) EQ 0) then begin x = state.brightness*(state.ncolors-1) y = state.contrast*(state.ncolors-1)*contrastscale > 2 endif else begin x = brightness*(state.ncolors-1) y = contrast*(state.ncolors-1)*contrastscale > 2 endelse endelse ;old version ;x = state.brightness*(state.ncolors-1) ;y = state.contrast*(state.ncolors-1) > 2 ; Minor change by AJB high = x+y & low = x-y diff = (high-low) > 1 slope = float(state.ncolors-1)/diff ;Scale to range of 0 : nc-1 intercept = -slope*low p = long(findgen(state.ncolors)*slope+intercept) ;subscripts to select tvlct, r_vector[p], g_vector[p], b_vector[p], 8 end ;------------------------------------------------------------------ pro atv_initcolors ; Load a simple color table with the basic 8 colors in the lowest ; 8 entries of the color table. Also set top color to white. common atv_state rtiny = [0, 1, 0, 0, 0, 1, 1, 1] gtiny = [0, 0, 1, 0, 1, 0, 1, 1] btiny = [0, 0, 0, 1, 1, 1, 0, 1] tvlct, 255*rtiny, 255*gtiny, 255*btiny tvlct, [255],[255],[255], !d.table_size-1 end ;-------------------------------------------------------------------- pro atv_getct, tablenum ; Read in a pre-defined color table, and invert if necessary. common atv_color common atv_state common atv_images atv_setwindow, state.draw_window_id loadct, tablenum, /silent, bottom=8 tvlct, r, g, b, 8, /get atv_initcolors r = r[0:state.ncolors-2] g = g[0:state.ncolors-2] b = b[0:state.ncolors-2] if (state.invert_colormap EQ 1) then begin r = reverse(r) g = reverse(g) b = reverse(b) endif r_vector = r g_vector = g b_vector = b atv_stretchct ; need this to re-set to external color table atv_resetwindow if (state.bitdepth EQ 24 AND (n_elements(pan_image) GT 10) ) then $ atv_refresh end ;-------------------------------------------------------------------- pro atv_makect, tablename ; Define new color tables here. Invert if necessary. common atv_state common atv_color case tablename of 'ATV Special': begin w = findgen(256) sigma = 60. center = 140 r = 255.* exp(-1.*(w - center)^2 / (2.*sigma^2)) r[center:255] = 255. sigma = 60 center = 255 g = 255. * exp(-1.*(w - center)^2 / (2.*sigma^2)) sigma = 60 center = 40 b = 255. * exp(-1.*(w - center)^2 / (2.*sigma^2)) b[0:center-1] = findgen(center)^0.5 / center^0.5 * 255. center = 30 b[(255-center+1):255] = findgen(center)^2 / center^2 *255. end 'Velocity2': begin r = fltarr(256) r[0:127] = 128. - findgen(128) r[128:255] = 255 g = fltarr(256) g[0:127] = findgen(128)^1.5 g[128:255] = reverse(g[0:127]) g = g / max(g) * 255. b = 255. - findgen(256) b[128:255] = findgen(128)^3 / 128.^2 end 'Velocity1': begin w = findgen(256) sigma = 25. center = 170 r = 255.* exp(-1.*(w - center)^2 / (2.*sigma^2)) r[center:255] = 255. sigma = 30. center = 0. r = r + 100.* exp(-1.*(w - center)^2 / (2.*sigma^2)) sigma = 30. center1 = 100. g = fltarr(256) g[0:center1] = 255. * exp(-1.*(w[0:center1] - center1)^2 / (2.*sigma^2)) sigma = 60. center2 = 140. g[center1:center2] = 255. g[center2:255] = $ 255. * exp(-1.*(w[center2:255] - center2)^2 / (2.*sigma^2)) sigma = 40. center = 70 b = 255.* exp(-1.*(w - center)^2 / (2.*sigma^2)) b[0:center] = 255. end ; add more color table definitions here as needed... else: return endcase r = congrid(r, state.ncolors) g = congrid(g, state.ncolors) b = congrid(b, state.ncolors) if (state.invert_colormap EQ 1) then begin r = reverse(r) g = reverse(g) b = reverse(b) endif r_vector = temporary(r) g_vector = temporary(g) b_vector = temporary(b) atv_stretchct ; need this to preserve external color map atv_resetwindow if (state.bitdepth EQ 24) then atv_refresh end ;---------------------------------------------------------------------- function atv_icolor, color ; Routine to reserve the bottom 8 colors of the color table ; for plot overlays and line plots. if (n_elements(color) EQ 0) then return, 1 ncolor = N_elements(color) ; If COLOR is a string or array of strings, then convert color names ; to integer values if (size(color,/tname) EQ 'STRING') then begin ; Test if COLOR is a string ; Detemine the default color for the current device if (!d.name EQ 'X') then defcolor = 7 $ ; white for X-windows else defcolor = 0 ; black otherwise icolor = 0 * (color EQ 'black') $ + 1 * (color EQ 'red') $ + 2 * (color EQ 'green') $ + 3 * (color EQ 'blue') $ + 4 * (color EQ 'cyan') $ + 5 * (color EQ 'magenta') $ + 6 * (color EQ 'yellow') $ + 7 * (color EQ 'white') $ + defcolor * (color EQ 'default') endif else begin icolor = long(color) endelse return, icolor end ;--------------------------------------------------------------------- ; routines dealing with image header, title, and related info ;-------------------------------------------------------------------- pro atv_settitle ; Update title bar with the image file name common atv_state if (state.title_extras EQ 'firstimage') then return sizestring = strcompress('(' + string(state.image_size[0]) + 'x' + $ string(state.image_size[1]) + ')', /remove_all) state.title_extras = strcompress(state.title_extras + ' ' + sizestring) if (state.imagename EQ '') then begin title = strcompress('atv: ' + state.title_extras) widget_control, state.base_id, tlb_set_title = title endif else begin slash = strpos(state.imagename, state.delimiter, /reverse_search) if (slash NE -1) then name = strmid(state.imagename, slash+1) $ else name = state.imagename title = strcompress('atv: '+ name + ' ' + state.title_extras) widget_control, state.base_id, tlb_set_title = title endelse end ;---------------------------------------------------------------------- pro atv_setheader, head ; Routine to keep the image header using a pointer to a ; heap variable. If there is no header (i.e. if atv has just been ; passed a data array rather than a filename), then make the ; header pointer a null pointer. Get astrometry info from the ; header if available. If there's no astrometry information, set ; state.astr_ptr to be a null pointer. common atv_state ; Kill the header info window when a new image is read in if (xregistered('atv_headinfo')) then begin widget_control, state.headinfo_base_id, /destroy endif if (xregistered('atv_stats')) then begin widget_control, state.stats_base_id, /destroy endif state.cunit = '' if (n_elements(head) LE 1) then begin ; If there's no image header... state.wcstype = 'none' ptr_free, state.head_ptr state.head_ptr = ptr_new() ptr_free, state.astr_ptr state.astr_ptr = ptr_new() widget_control, state.wcs_bar_id, set_value = '---No WCS Info---' return endif ptr_free, state.head_ptr state.head_ptr = ptr_new(head) ; Get astrometry information from header, if it exists ptr_free, state.astr_ptr ; kill previous astrometry info state.astr_ptr = ptr_new() extast, head, astr, noparams ; No valid astrometry in header if (noparams EQ -1) then begin widget_control, state.wcs_bar_id, set_value = '---No WCS Info---' state.wcstype = 'none' return endif ; Here: add escape clauses for any WCS types that cause crashes. Add ; more as needed checkastr = strcompress(string(astr.ctype[0]), /remove_all) if ( (checkastr EQ 'PIXEL') OR $ (checkastr EQ '') OR $ (checkastr EQ 'COLUMN#') ) then begin widget_control, state.wcs_bar_id, set_value = '---No WCS Info---' state.wcstype = 'none' return endif if (checkastr EQ 'RA---TNX') then begin widget_control, state.wcs_bar_id, set_value = '---No WCS Info---' state.wcstype = 'none' print print, 'WARNING- WCS info is in unsupported TNX format.' return endif ; Image is a 2-d calibrated spectrum: ; (these keywords work for HST STIS 2-d spectral images) if (astr.ctype[0] EQ 'LAMBDA' OR astr.ctype[0] EQ 'WAVE') then begin state.wcstype = 'lambda' state.astr_ptr = ptr_new(astr) widget_control, state.wcs_bar_id, set_value = ' ' state.cunit = sxpar(*state.head_ptr, 'cunit1') state.cunit = strcompress(string(state.cunit), /remove_all) if (state.cunit NE '0') then begin state.cunit = strcompress(strupcase(strmid(state.cunit,0,1)) + $ strmid(state.cunit,1), $ /remove_all) endif else begin state.cunit = '' endelse return endif ; 2-D wavelength calibrated spectrum from iraf gemini reductions: if (string(sxpar(head, 'WAT1_001')) EQ $ 'wtype=linear label=Wavelength units=angstroms') then begin state.wcstype = 'lambda' state.astr_ptr = ptr_new(astr) widget_control, state.wcs_bar_id, set_value = ' ' state.cunit = 'Angstrom' return endif ; final error check on WCS, in case it's in a format that can't be ; understood by the idlastro routines. catch, error_status if (error_status NE 0) then begin print print, 'Warning: WCS information could not be understood.' wcsstring = '---No WCS Info---' state.wcstype='none' return endif ; see if coordinates can be extracted without an error xy2ad, 0, 0, astr, lon, lat catch, /cancel ; Good astrometry info in header: state.wcstype = 'angle' widget_control, state.wcs_bar_id, set_value = ' ' ; Check for GSS type header if strmid( astr.ctype[0], 5, 3) EQ 'GSS' then begin hdr1 = head gsss_STDAST, hdr1 extast, hdr1, astr, noparams endif ; Create a pointer to the header info state.astr_ptr = ptr_new(astr) ; Get the equinox of the coordinate system equ = get_equinox(head, code) if (code NE -1) then begin if (equ EQ 2000.0) then state.equinox = 'J2000' if (equ EQ 1950.0) then state.equinox = 'B1950' if (equ NE 2000.0 and equ NE 1950.0) then $ state.equinox = string(equ, format = '(f6.1)') endif else begin IF (strmid(astr.ctype[0], 0, 4) EQ 'GLON') THEN BEGIN state.equinox = 'J2000' ; (just so it is set) ENDIF ELSE BEGIN ; If no valid equinox, then ignore the WCS info. print, 'Warning: WCS equinox not given in image header. Ignoring WCS info.' ptr_free, state.astr_ptr ; clear pointer state.astr_ptr = ptr_new() state.equinox = 'J2000' state.wcstype = 'none' widget_control, state.wcs_bar_id, set_value = '---No WCS Info---' ENDELSE endelse ; Set default display to native system in header state.display_equinox = state.equinox state.display_coord_sys = strmid(astr.ctype[0], 0, 4) end ;--------------------------------------------------------------------- pro atv_headinfo common atv_state ; If there's no header, kill the headinfo window and exit this ; routine. if (not(ptr_valid(state.head_ptr))) then begin if (xregistered('atv_headinfo')) then begin widget_control, state.headinfo_base_id, /destroy endif atv_message, 'No header information available for this image!', $ msgtype = 'error', /window return endif ; If there is header information but not headinfo window, ; create the headinfo window. if (not(xregistered('atv_headinfo', /noshow))) then begin headinfo_base = $ widget_base(/base_align_right, $ group_leader = state.base_id, $ /column, $ title = 'atv image header information', $ uvalue = 'headinfo_base') state.headinfo_base_id = headinfo_base h = *(state.head_ptr) headinfo_text = widget_text(headinfo_base, $ /scroll, $ value = h, $ xsize = 85, $ ysize = 24) headinfo_done = widget_button(headinfo_base, $ value = 'Done', $ uvalue = 'headinfo_done') widget_control, headinfo_base, /realize xmanager, 'atv_headinfo', headinfo_base, /no_block endif end ;--------------------------------------------------------------------- pro atv_headinfo_event, event common atv_state widget_control, event.id, get_uvalue = uvalue case uvalue of 'headinfo_done': widget_control, event.top, /destroy else: endcase end ;---------------------------------------------------------------------- ; routines to do plot overlays ;---------------------------------------------------------------------- pro atv_plot1plot, iplot common atv_pdata common atv_state ; Plot a point or line overplot on the image atv_setwindow, state.draw_window_id widget_control, /hourglass oplot, [(*(plot_ptr[iplot])).x], [(*(plot_ptr[iplot])).y], $ _extra = (*(plot_ptr[iplot])).options atv_resetwindow state.newrefresh=1 end ;---------------------------------------------------------------------- pro atv_plot1text, iplot common atv_pdata common atv_state ; Plot a text overlay on the image atv_setwindow, state.draw_window_id widget_control, /hourglass xyouts, (*(plot_ptr[iplot])).x, (*(plot_ptr[iplot])).y, $ (*(plot_ptr[iplot])).text, _extra = (*(plot_ptr[iplot])).options atv_resetwindow state.newrefresh=1 end ;---------------------------------------------------------------------- pro atv_plot1arrow, iplot common atv_pdata common atv_state ; Plot a arrow overlay on the image atv_setwindow, state.draw_window_id widget_control, /hourglass arrow, (*(plot_ptr[iplot])).x1, (*(plot_ptr[iplot])).y1, $ (*(plot_ptr[iplot])).x2, (*(plot_ptr[iplot])).y2, $ _extra = (*(plot_ptr[iplot])).options, /data atv_resetwindow state.newrefresh=1 end ;---------------------------------------------------------------------- function atv_degperpix, hdr ; This program calculates the pixel scale (deg/pixel) and returns the value common atv_state On_error,2 ;Return to caller extast, hdr, bastr, noparams ;extract astrom params in deg. a = bastr.crval[0] d = bastr.crval[1] factor = 60.0 ;conversion factor from deg to arcmin d1 = d + (1/factor) ;compute x,y of crval + 1 arcmin proj = strmid(bastr.ctype[0],5,3) case proj of 'GSS': gsssadxy, bastr, [a,a], [d,d1], x, y else: ad2xy, [a,a], [d,d1], bastr, x, y endcase dmin = sqrt( (x[1]-x[0])^2 + (y[1]-y[0])^2 ) ;det. size in pixels of 1 arcmin ; Convert to degrees per pixel and return scale degperpix = 1. / dmin / 60. return, degperpix end ;---------------------------------------------------------------------- function atv_wcs2pix, coords, coord_sys=coord_sys, line=line common atv_state ; check validity of state.astr_ptr and state.head_ptr before ; proceeding to grab wcs information if ptr_valid(state.astr_ptr) then begin ctype = (*state.astr_ptr).ctype equinox = state.equinox disp_type = state.display_coord_sys disp_equinox = state.display_equinox disp_base60 = state.display_base60 bastr = *(state.astr_ptr) ; function to convert an ATV region from wcs coordinates to pixel coordinates degperpix = atv_degperpix(*(state.head_ptr)) ; need numerical equinox values IF (equinox EQ 'J2000') THEN num_equinox = 2000.0 ELSE $ IF (equinox EQ 'B1950') THEN num_equinox = 1950.0 ELSE $ num_equinox = float(equinox) headtype = strmid(ctype[0], 0, 4) n_coords = n_elements(coords) endif case coord_sys of 'j2000': begin if (strpos(coords[0], ':')) ne -1 then begin ra_arr = strsplit(coords[0],':',/extract) dec_arr = strsplit(coords[1],':',/extract) ra = ten(float(ra_arr[0]), float(ra_arr[1]), $ float(ra_arr[2])) * 15.0 dec = ten(float(dec_arr[0]), float(dec_arr[1]), $ float(dec_arr[2])) if (keyword_set(line)) then begin ra1_arr = strsplit(coords[2],':',/extract) dec1_arr = strsplit(coords[3],':',/extract) ra1 = ten(float(ra1_arr[0]), float(ra1_arr[1]), $ float(ra1_arr[2])) * 15.0 dec1 = ten(float(dec1_arr[0]), float(dec1_arr[1]), $ float(dec1_arr[2])) endif endif else begin ; coordinates in degrees ra=float(coords[0]) dec=float(coords[1]) if (keyword_set(line)) then begin ra1=float(coords[2]) dec1=float(coords[3]) endif endelse if (not keyword_set(line)) then begin if (n_coords ne 6) then $ coords[2:n_coords-2] = $ strcompress(string(float(coords[2:n_coords-2]) / $ (degperpix * 60.)),/remove_all) $ else $ coords[2:n_coords-3] = $ strcompress(string(float(coords[2:n_coords-3]) / $ (degperpix * 60.)),/remove_all