; $Id: satplot.pro,v 1.3 2024/10/08 19:35:48 nathan Exp $ ; $Log: satplot.pro,v $ ; Revision 1.3 2024/10/08 19:35:48 nathan ; contents of SATPLOT_2.3.tar.gz ; ; Revision 2.3 2024/09/09 00:00:00 penteado ; Updating satplot package to V2.3, added documentation ; ; "Copyright 2012, by the California Institute of Technology. ; ALL RIGHTS RESERVED. United States Government Sponsorship ; acknowledged. Any commercial use must be negotiated with the ; Office of Technology Transfer at the California Institute of ; Technology. ; ; This software may be subject to U.S. export control laws. ; By accepting this software, the user agrees to comply with ; all applicable U.S. export laws and regulations. User has ; the responsibility to obtain export licenses, or other ; export authority as may be required before exporting such ; information to foreign countries or providing access to ; foreign persons." ;+ ; satplot Version 2.3 ; ; Example: ; ; satplot,'jplot_20070831_000000__20070902_233000.png' ; file name breakdown: ; 'jplot__' with double underscore ; yyyymmdd date of interval start ; single underscore ; hhmmss time of interval start ; double underscore ; yyyymmdd date of interval end ; single underscore ; hhmmss time of interval end ; ; Notes: ; ; Satplot (Solar Angle-Time plot) is one tool in a package designed to construct and analyze J-plots. ; Other tools include (in order of execution): ; ; Data pre-processing: ; ; jdiff.pro Create difference maps for COR2. ; jsrem.pro Create difference maps for HI_1 and HI_2. ; jmap.pro Create Platte Carre (simple cylindrical) maps from the difference images. ; These jmaps have twice latitudinal resolution, representing angle from sun center. ; These jmaps are composites of COR2, HI_1 and HI_2. It is from these jmaps that a strip ; (of Delta width, starting a Position Angle) is compressed into a column for the jplot. ; jcount.pro Count all of above elements for entire STEREO mission since launch. ; These counts help determine if sufficient data is available to create the desired jplot. ; ; Data analysis: ; ; jpredict.pro Highlight a strip in a jmap for given datetime, Position Angle and Delta. ; jplot.pro Create a jplot (columns) for given datetime, Position Angle and Delta. ; A jplot may be of any time span, longer time spans just result in wider jplots. ; satplot.pro View and analyze a jplot, select tiepoints, save picture, save H-T plot and save report. ; anaht5.pro (AN EXAMPLE, NOT INCLUDED IN PACKAGE) analyse height-time plots, estimate t-0. ; ; Additional required utilities: ; ; get_closest_jmap_filename.pro (required by: jplot.pro jpredict.pro satplot.pro) ; get_closest_fits_filename.pro (required by: jmap.pro) ; get_closest_srem_filename.pro (required by: jmap.pro satplot.pro) ; get_closest_tiff_filename.pro (required by: jmap.pro) ; rotate_vector.pro (required by: jmap.pro) ; chain_link_utilities.pro (required by: satplot.pro) ; ; Terminology: ; ; jdiff - Difference images for COR2 ; jsrem - Difference images for HI_1 and HI_2. ; jmap - Image that is a cylindrical map, composited from COR2, HI_1 and HI_2. ; jplot - Image where rows represent angle from sun center, and columns represent time steps (cadence). ; satplot - Tool for analyzing a jplot. Also prefix for saved picture, plot and report that the tool can generate. ; saved picture - PNG picture of main satplot window. ; saved plot - PNG picture of Angle-Time plot (similar to a Height-Time plot.) ; saved report - Text file of tiepoints and meta data (similar to Height-Time file, and is called *.ht) ; curvefit - Curve fit program by Chris Mostl, Mar 2012, produces two fit methods: Fixed Phi and Harmonic Mean. ;- @chain_link_utilities.pro FUNCTION satplot::ANGLE2TEL,angle CASE 1 OF (FLOAT(angle) GT SELF.hi_2_min_angle) : tel='hi_2' (FLOAT(angle) GT SELF.hi_1_min_angle) AND (angle LE SELF.hi_2_min_angle) : tel='hi_1' (FLOAT(angle) GT SELF.cor2_min_angle) AND (angle LE SELF.hi_1_min_angle) : tel='cor2' (FLOAT(angle) LE SELF.cor2_min_angle) : tel='cor1' ENDCASE RETURN,STRLOWCASE(tel) END FUNCTION satplot::get_color,x,y curwin=SELF.draw_window_id WSET,SELF.draw_window_id ;------------------------------------------------------- ; Make color black or white depending on the image value ; of an averaged area centered at the tiepoint location. ;------------------------------------------------------- ; buffer=0 ; xsub=(x*(x LE buffer))+(buffer*(x GT buffer)) ; ysub=(y*(y LE buffer))+(buffer*(y GT buffer)) ; xtmp=(SELF.x_dataelements-1)-x ; xadd=(xtmp*(xtmp LE buffer))+(buffer*(xtmp GT buffer)) ; ytmp=(SELF.y_dataelements-1)-y ; yadd=(ytmp*(ytmp LE buffer))+(buffer*(ytmp GT buffer)) ; chip=(*SELF.jplot_image_ptr)[x-xsub:x+xadd,y-ysub:y+yadd] ; color=(!D.N_COLORS-1)*(MEAN(chip) LT 128) color=(!D.N_COLORS-1)*((*SELF.jplot_image_ptr)[x,y] LT 128) WSET,curwin RETURN,color END PRO satplot::plot_all_tiepoints,UPDATE_PLOT=update_plot IF (SIZE(update_plot,/TYPE) EQ 0) THEN BEGIN update_plot=0 ENDIF IF ((SELF.actual_time_plot EQ 1) AND (SELF.plot_window_is_onscreen EQ 1) AND (update_plot EQ 1) AND (CHAIN_LINK_COUNTER(SELF.chain) EQ 0)) THEN BEGIN ; No tiepoints, update plot with this message. curwin=!D.WINDOW ; Use WINDOW instead of WSET so that RETAIN=2 can be invoked, which is critical for plotting. WINDOW,SELF.plot_window_id,RETAIN=2 ERASE,!D.N_COLORS-1 XYOUTS,0.5,0.5,'NO TIEPOINTS',ALIGN=0.5,CHARSIZE=5,COLOR=0,/NORMAL WSET,curwin ENDIF ;------------------------------------------------------- ; Plot all the tiepoints. ;------------------------------------------------------- SELF.chain=CHAIN_START_FINDER(SELF.chain) n_chain_links=CHAIN_LINK_COUNTER(SELF.chain) IF (n_chain_links EQ 0) THEN BEGIN RETURN ENDIF column=LONARR(n_chain_links) rowrow=LONARR(n_chain_links) color=LONARR(n_chain_links) actual_time_values=STRARR(n_chain_links) actual_jd_values=DBLARR(n_chain_links) observed_angle=DBLARR(n_chain_links) x_points=LONARR(n_chain_links) y_points=LONARR(n_chain_links) colors=BYTARR(n_chain_links) FOR i=0,n_chain_links-1 DO BEGIN actual_time_values[i]=(*(*SELF.chain).data)[1] data_string=(*(*SELF.chain).data)[0] data_string_elements=STRSPLIT(data_string,' ',/EXTRACT) column[i]=data_string_elements[N_ELEMENTS(data_string_elements)-2] rowrow[i]=data_string_elements[N_ELEMENTS(data_string_elements)-1] colors[i]=SELF->get_color(column[i],rowrow[i]) x_points[i]=SELF.x1_margin+((column[i]-SELF.x_start_value)*SELF.zoom_value)+(SELF.zoom_value/2) y_points[i]=SELF.y1_margin+((rowrow[i]-SELF.y_start_value)*SELF.zoom_value)+(SELF.zoom_value/2) IF PTR_VALID((*SELF.chain).next) THEN BEGIN SELF.chain=(*SELF.chain).next ENDIF ENDFOR valid_ndx=WHERE((x_points GE SELF.x1_margin) AND (x_points LE (SELF.plot_x_pixels+SELF.x1_margin)) $ AND (y_points GE SELF.y1_margin) AND (y_points LE (SELF.plot_y_pixels+SELF.y1_margin)),valid_count) IF (valid_count GT 0) THEN BEGIN x_points=x_points[valid_ndx] y_points=y_points[valid_ndx] colors=colors[valid_ndx] curwin=!D.WINDOW WSET,SELF.draw_window_id ; Plot tiepoints over jplot. PLOTS,x_points,y_points,PSYM=7,COLOR=colors,/DEVICE IF ((SELF.actual_time_plot EQ 1) AND (SELF.plot_window_is_onscreen EQ 1) AND (update_plot EQ 1)) THEN BEGIN ; In a separate plotting window, plot the actual FITS times from which each jpixel was derived. FOR which_point=0,n_chain_links-1 DO BEGIN observed_angle[which_point]=(*SELF.observed_angle_master_ptr)[rowrow[which_point]] actual_time_jd=ANYTIM2JD(actual_time_values[which_point]) actual_jd_values[which_point]=actual_time_jd.(0)+actual_time_jd.(1) ENDFOR ;------------------------------------------------------------------------------------------------------- ; Kluge to force major tick marks to midnight instead of noon as per julian date convention. ; Artificially set time back by 12 hours. actual_jd_values=actual_jd_values-0.5d ;------------------------------------------------------------------------------------------------------- xmin=MIN(actual_jd_values)-((MAX(actual_jd_values)-MIN(actual_jd_values))*0.1) xmax=MAX(actual_jd_values)+((MAX(actual_jd_values)-MIN(actual_jd_values))*0.1) ymin=MIN(observed_angle)-((MAX(observed_angle)-MIN(observed_angle))*0.1) ymax=MAX(observed_angle)+((MAX(observed_angle)-MIN(observed_angle))*0.1) ; Format the first time for plot title. ; first_actual_time=(actual_time_values[WHERE(actual_jd_values EQ MIN(actual_jd_values))])[0] ; first_actual_year=STRMID(first_actual_time,0,4) ; first_actual_month=STRMID(first_actual_time,5,2) ; first_actual_day=STRMID(first_actual_time,8,2) ; first_actual_hour=STRMID(first_actual_time,11,2) ; first_actual_minute=STRMID(first_actual_time,14,2) ; first_actual_formatted_date=first_actual_month+'/'+first_actual_day+'/'+first_actual_year ; first_actual_formatted_time=first_actual_hour+':'+first_actual_minute ; first_actual_formatted_datetime=first_actual_formatted_date+' '+first_actual_formatted_time ;------------------------------------------------------------------------------------------------------- ; Kluge to force major tick marks to midnight instead of noon as per julian date convention. ; Re-correct time forward by 12 hours. title_jd=xmin+0.5d ;------------------------------------------------------------------------------------------------------- CALDAT,title_jd,title_month,title_day,title_year,title_hour,title_minute IF title_hour LT 10 THEN title_hour='0'+STRTRIM(title_hour,2) ELSE title_hour=STRTRIM(title_hour,2) IF title_minute LT 10 THEN title_minute='0'+STRTRIM(title_minute,2) ELSE title_minute=STRTRIM(title_minute,2) title_datetime=STRTRIM(title_month,2)+'/'+STRTRIM(title_day,2)+'/'+STRTRIM(title_year,2)+' '+title_hour+':'+title_minute curwin=!D.WINDOW ; Use WINDOW instead of WSET so that RETAIN=2 can be invoked, which is critical for plotting. WINDOW,SELF.plot_window_id,RETAIN=2 !P.MULTI=0 ; default window is wider (x > y), use full window. curvefit window is taller (x < y), use upper half of window. CASE !D.X_SIZE GT !D.Y_SIZE OF 0 : plot_position=[SELF.x1_position,SELF.y2_position/2,SELF.x2_position,SELF.y2_position] 1 : plot_position=[SELF.x1_position,SELF.y1_position,SELF.x2_position,SELF.y2_position] ENDCASE ; PLOT,[actual_jd_values],[observed_angle],TITLE=first_actual_formatted_datetime+$ PLOT,[actual_jd_values],[observed_angle],TITLE=title_datetime+$ ', PA '+STRTRIM(SELF.position_angle,2)+', D '+STRTRIM(SELF.delta,2)+', '+SELF.spacecraft,$ XTITLE='Date_Obs',$ YTITLE='Elongation (degrees)',CHARSIZE=1.5,PSYM=7,$ XTICKFORMAT='tick_string_function',XRANGE=[xmin,xmax],YRANGE=[ymin,ymax],XSTYLE=1,YSTYLE=1,$ COLOR=0,BACKGROUND=!D.N_COLORS-1,POSITION=plot_position ; Draw the least squares line (only if 2 or more points). IF (CHAIN_LINK_COUNTER(SELF.chain) GT 1) THEN BEGIN time_sorted_ndx=SORT(actual_jd_values) time=actual_jd_values[time_sorted_ndx] height=observed_angle[time_sorted_ndx] nfeatures=CHAIN_LINK_COUNTER(SELF.chain) slope = (REGRESS( (time - time[0]), height, CONST=offset, /DOUBLE ))[0] ;help,time,nfeatures,(time[nfeatures-1])[0] OPLOT, [time[0],time[nfeatures-1]], [offset,(time[nfeatures-1]-time[0])*slope+offset],LINESTYLE=1,COLOR=0 ENDIF ; Enable the curvefit button (only if 2 or more points). IF (CHAIN_LINK_COUNTER(SELF.chain) GE SELF.min_tiepoints_for_curvefit) THEN BEGIN WIDGET_CONTROL,SELF.curvefit_button,SENSITIVE=1 WIDGET_CONTROL,SELF.curvefit_label,SENSITIVE=0 ENDIF ELSE BEGIN WIDGET_CONTROL,SELF.curvefit_button,SENSITIVE=0 WIDGET_CONTROL,SELF.curvefit_label,SENSITIVE=1 ENDELSE WSET,curwin ; Update point counter. point_count=STRMID(SELF.point_count_string, 0, STRLEN(SELF.point_count_string)-STRLEN(STRTRIM(CHAIN_LINK_COUNTER(SELF.chain),2))) + STRTRIM(CHAIN_LINK_COUNTER(SELF.chain),2) WIDGET_CONTROL,SELF.tiepoint_count_box,SET_VALUE=point_count ENDIF ENDIF END PRO satplot::calculate_everything curwin=SELF.draw_window_id WSET,SELF.draw_window_id ; Normal coordinates: x1 y1 x2 y2. Corners of the plot space where the axis start/end. SELF.x1_position=SELF.x1_margin/!D.X_SIZE SELF.y1_position=SELF.y1_margin/!D.Y_SIZE SELF.x2_position=(!D.X_SIZE-SELF.x2_margin)/!D.X_SIZE SELF.y2_position=(!D.Y_SIZE-SELF.y2_margin)/!D.Y_SIZE ; Pixel coordinates for TV command X,Y image placement. Aligns with plot space. SELF.x_plotspace=!D.X_SIZE-(SELF.x1_margin+SELF.x2_margin) SELF.y_plotspace=!D.Y_SIZE-(SELF.y1_margin+SELF.y2_margin) ; Calculate pixels inside plot axes, or size of (zoomed) image, whichever is lesser. SELF.plot_x_pixels=(SELF.x_plotspace) < ROUND(SELF.x_dataelements*SELF.zoom_value) SELF.plot_y_pixels=(SELF.y_plotspace) < ROUND(SELF.y_dataelements*SELF.zoom_value) ; This fixes the special case of enlarging the window when x_start_slider is all the way to the right. IF (WIDGET_INFO(SELF.x_start_slider,/VALID_ID) EQ 1) THEN SELF->set_zoom_value,SELF.zoom_value ; This fixes the special case of enlarging the window when y_start_slider is all the way to the top. IF (WIDGET_INFO(SELF.y_start_slider,/VALID_ID) EQ 1) THEN SELF->set_zoom_value,SELF.zoom_value ; Subsetting values for trimming image input to CONGRID command. Subsetting speeds up redraw. SELF.subset_xs=SELF.x_start_value ; SELF.subset_xe=(SELF.subset_xs+(SELF.x_plotspace-1)/SELF.zoom_value+SELF.zoom_value) < (SELF.x_dataelements-1) SELF.subset_xe=(SELF.subset_xs+(SELF.x_plotspace-1)/SELF.zoom_value) < (SELF.x_dataelements-1) SELF.subset_ys=SELF.y_start_value ; SELF.subset_ye=(SELF.subset_ys+(SELF.y_plotspace-1)/SELF.zoom_value+SELF.zoom_value) < (SELF.y_dataelements-1) SELF.subset_ye=(SELF.subset_ys+(SELF.y_plotspace-1)/SELF.zoom_value) < (SELF.y_dataelements-1) ; Y axis. IF (PTR_VALID(SELF.observed_angle_ptr)) THEN BEGIN PTR_FREE,SELF.observed_angle_ptr ENDIF SELF.y_end_value=SELF.y_start_value+(SELF.plot_y_pixels/SELF.zoom_value)-1 SELF.observed_angle_ptr=PTR_NEW((*SELF.observed_angle_master_ptr)[SELF.y_start_value:SELF.y_end_value]) ; X axis. IF (PTR_VALID(SELF.time_ptr)) THEN PTR_FREE,SELF.time_ptr current_start_time=(*SELF.time_master_ptr)[SELF.subset_xs] current_time_span=(*SELF.time_master_ptr)[SELF.subset_xe]-current_start_time time_axis_length=ROUND(SELF.plot_x_pixels/SELF.zoom_value) SELF.time_ptr=PTR_NEW(((DINDGEN(time_axis_length)/(time_axis_length-1))*current_time_span)+current_start_time) ; Format the date for plot title. CALDAT,(*SELF.time_ptr)[0],month,day,year,hour,minute,sec IF month LT 10 THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF day LT 10 THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF (hour LT 10) THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF (minute LT 10) THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) SELF.formatted_datetime=STRTRIM(year,2)+'-'+STRTRIM(month,2)+'-'+STRTRIM(day,2)+' '+hour+':'+minute ;------------------------------------------------------------------------------------------------------- ; Kluge to force major tick marks to midnight instead of noon as per julian date convention. ; See matching (*SELF.time_ptr)+0.5d in tick_string_function and readout functions. ; Artificially set time back by 12 hours. *SELF.time_ptr=(*SELF.time_ptr)-0.5d ;------------------------------------------------------------------------------------------------------- new_xsize=ROUND((SELF.subset_xe-SELF.subset_xs+1)*SELF.zoom_value) xmin_ndx=0 xmax_ndx=(N_ELEMENTS(*SELF.time_ptr)-1) < ((SELF.plot_x_pixels-1) < (new_xsize-1)) SELF.xmin=(*SELF.time_ptr)[xmin_ndx] SELF.xmax=(*SELF.time_ptr)[xmax_ndx] IF (SELF.x_plotspace GT SELF.plot_x_pixels) THEN BEGIN ; Extend xmax to end of x-axis on plot because time_ptr only goes to end of jplot image data. SELF.xmax=SELF.xmin+((*SELF.time_ptr)[N_ELEMENTS(*SELF.time_ptr)-1]-SELF.xmin)*(SELF.x_plotspace/DOUBLE(SELF.plot_x_pixels)) ENDIF SELF.ymin=(*SELF.observed_angle_ptr)[0] SELF.ymax=(*SELF.observed_angle_ptr)[N_ELEMENTS(*SELF.observed_angle_ptr)-1] WSET,curwin END PRO satplot::set_x_start_value,value ; Convert slider value into pixels, where maximum value is image size minus plot size accounting for zoom. SELF.x_start_value=ROUND((value/DOUBLE(SELF.x_start_slider_max))*(SELF.x_dataelements-(SELF.plot_x_pixels/SELF.zoom_value))) END PRO satplot::set_y_start_value,value,THIS_IS_A_SLIDER_EVENT=this_is_a_slider_event ; Convert slider value into pixels, where maximum value is image size minus plot size accounting for zoom. SELF.y_start_value=ROUND((value/DOUBLE(SELF.y_start_slider_max))*(SELF.y_dataelements-(SELF.plot_y_pixels/SELF.zoom_value))) IF (KEYWORD_SET(this_is_a_slider_event)) THEN BEGIN SELF->calculate_everything WIDGET_CONTROL,SELF.y_start_label,SET_VALUE=STRMID(STRTRIM((*SELF.observed_angle_ptr)[0],2),0,5) ENDIF END FUNCTION satplot::get_zoom_value RETURN,SELF.zoom_value END FUNCTION satplot::get_zoom_scale RETURN,SELF.zoom_scale END FUNCTION satplot::get_zoom_label RETURN,SELF.zoom_label END PRO satplot::set_zoom_value,value ; SELF.zoom_value=ROUND(value*10.0)/10.0 SELF.zoom_value=value IF ((SELF.x_dataelements-ROUND(SELF.plot_x_pixels/SELF.zoom_value)) GT 0) THEN BEGIN ; Adjust the x_start slider position as zoom changes. new_setting=FLOAT(SELF.x_start_value) new_setting=new_setting/(SELF.x_dataelements-ROUND(SELF.plot_x_pixels/SELF.zoom_value)) new_setting=(new_setting*SELF.x_start_slider_max) < SELF.x_start_slider_max WIDGET_CONTROL,SELF.x_start_slider,SET_VALUE=new_setting ; Adjust the x_start value. SELF->set_x_start_value,new_setting ENDIF ELSE BEGIN WIDGET_CONTROL,SELF.x_start_slider,SET_VALUE=0 SELF->set_x_start_value,0 ENDELSE IF ((SELF.y_dataelements-ROUND(SELF.plot_y_pixels/SELF.zoom_value)) GT 0) THEN BEGIN ; Adjust the y_start slider position as zoom changes. new_setting=FLOAT(SELF.y_start_value) new_setting=new_setting/(SELF.y_dataelements-ROUND(SELF.plot_y_pixels/SELF.zoom_value)) new_setting=(new_setting*SELF.y_start_slider_max) < SELF.y_start_slider_max WIDGET_CONTROL,SELF.y_start_slider,SET_VALUE=new_setting ; Adjust the y_start value. SELF->set_y_start_value,new_setting ENDIF ELSE BEGIN WIDGET_CONTROL,SELF.y_start_slider,SET_VALUE=0 SELF->set_y_start_value,0 ENDELSE END FUNCTION satplot::get_jd_first RETURN,SELF.jd_first END PRO zoom_slider_eh,event ; IDL object code does not allow an event handler to be a method. Work-around: get the object ID and call methods. IF (event.DRAG EQ 0) THEN RETURN WIDGET_CONTROL,event.TOP,GET_UVALUE=self_obj self_obj->set_zoom_value,event.VALUE*self_obj->get_zoom_scale() WIDGET_CONTROL,self_obj->get_zoom_label(),SET_VALUE='Zoom '+STRMID(STRTRIM(self_obj->get_zoom_value(),2),0,3) self_obj->redraw END PRO x_start_slider_eh,event ; IDL object code does not allow an event handler to be a method. Work-around: get the object ID and call methods. IF (event.DRAG EQ 0) THEN RETURN WIDGET_CONTROL,event.TOP,GET_UVALUE=self_obj self_obj->set_x_start_value,event.VALUE self_obj->redraw END PRO y_start_slider_eh,event ; IDL object code does not allow an event handler to be a method. Work-around: get the object ID and call methods. IF (event.DRAG EQ 0) THEN RETURN WIDGET_CONTROL,event.TOP,GET_UVALUE=self_obj self_obj->set_y_start_value,event.VALUE,/this_is_a_slider_event self_obj->redraw END FUNCTION tick_string_function,axis,index,value ; Plotter tick function to return correct string per tickmark. ;------------------------------------------------------------------------------------------------------- ; Kluge to force major tick marks to midnight instead of noon as per julian date convention. ; See matching (*SELF.time_ptr)-0.5d in redraw routine. ; Re-correct time forward by 12 hours. value=value+0.5d ;------------------------------------------------------------------------------------------------------- CALDAT,value,month,day,year,hour,minute,sec ; Fix an odd problem where sometimes time is one minute early. minute=CEIL(minute) IF (minute GE 59) THEN BEGIN minute=0 hour=hour+1 ENDIF ; Pad the minute string with a zero, if necessary. IF (minute LT 10) THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF ((hour EQ 0 OR hour EQ 24) AND minute EQ 0) THEN BEGIN ; Date. tick_string=STRTRIM(month,2)+'/'+STRTRIM(day,2)+'/'+STRMID(STRTRIM(year,2),2,2) ENDIF ELSE BEGIN ; Time. tick_string=STRTRIM(hour,2)+':'+minute ENDELSE RETURN,tick_string END PRO satplot::redraw,UPDATE_PLOT=update_plot curwin=SELF.draw_window_id WSET,SELF.draw_window_id SELF->calculate_everything ERASE,!D.N_COLORS-1 new_xsize=ROUND((SELF.subset_xe-SELF.subset_xs+1)*SELF.zoom_value) new_ysize=ROUND((SELF.subset_ye-SELF.subset_ys+1)*SELF.zoom_value) img=CONGRID((*SELF.jplot_image_ptr)[SELF.subset_xs:SELF.subset_xe,SELF.subset_ys:SELF.subset_ye],new_xsize,new_ysize) xe=(SELF.plot_x_pixels-1) < (new_xsize-1) ye=(SELF.plot_y_pixels-1) < (new_ysize-1) TV,img[0:xe,0:ye],SELF.x1_margin,SELF.y1_margin XYOUTS,0.5,1.0-SELF.y2_margin/!D.Y_SIZE*0.4,'Angle vs. Time',CHARSIZE=3,COLOR=0,/NORMAL,ALIGN=0.5 PLOT,*SELF.time_ptr,*SELF.observed_angle_ptr,TITLE=SELF.formatted_datetime+ $ ', PA '+STRTRIM(SELF.position_angle,2)+', D '+STRTRIM(SELF.delta,2)+', '+SELF.spacecraft,$ XTITLE='Time ('+STRTRIM(ROUND(60*24*SELF.jd_cadence),2)+' Minutes per Pixel Column)',$ YTITLE='Elongation (degrees)',CHARSIZE=2,$ XTICKFORMAT='tick_string_function',XRANGE=[SELF.xmin,SELF.xmax],YRANGE=[SELF.ymin,SELF.ymax],XSTYLE=1,YSTYLE=1,$ POSITION=[SELF.x1_position,SELF.y1_position,SELF.x2_position,SELF.y2_position],$ COLOR=0,BACKGROUND=!D.N_COLORS-1,/NODATA,/NOERASE SELF->plot_all_tiepoints,UPDATE_PLOT=update_plot ; When image data exceeds plot space, activate x-start and/or y-start sliders. WIDGET_CONTROL,SELF.x_start_slider,SENSITIVE=(SELF.x_dataelements GT ROUND(SELF.x_plotspace/SELF.zoom_value)) WIDGET_CONTROL,SELF.y_start_slider,SENSITIVE=(SELF.y_dataelements GT ROUND(SELF.y_plotspace/SELF.zoom_value)) ; Activate or de-activate depending on if any tiepoints exist in the chain. WIDGET_CONTROL,SELF.save_plot_button,SENSITIVE=PTR_VALID(SELF.chain) WIDGET_CONTROL,SELF.save_report_button,SENSITIVE=PTR_VALID(SELF.chain) WSET,curwin END FUNCTION satplot::get_output_filename ; file name breakdown: ; 'satplot__' with double underscore ; yyyymmdd date of interval start ; single underscore ; hhmmss time of interval start ; double underscore ; yyyymmdd date of interval end ; single underscore ; hhmmss time of interval end ; double underscore ; 'pa' stands for position angle ; value of position angle can be 1-3 digits ; single underscore ; 'd' stands for delta or width, can by 1-3 digits ; single underscore ; 'A' or 'B' stands for STEREO-A or STEREO-B ; '.ht' file name extension stands for height time. CALDAT,(*SELF.time_master_ptr)[0],month,day,year,hour,minute,sec print,month,day,year,hour,minute,sec IF (month LT 10) THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF (day LT 10) THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF (hour LT 10) THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF (minute LT 10) THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF (sec LT 10) THEN sec='0'+STRTRIM(FIX(sec),2) ELSE sec=STRTRIM(FIX(sec),2) begin_date=STRTRIM(year,2)+month+day+'_'+hour+minute+sec CALDAT,(*SELF.time_master_ptr)[N_ELEMENTS(*SELF.time_master_ptr)-1],month,day,year,hour,minute,sec print,month,day,year,hour,minute,sec IF (month LT 10) THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF (day LT 10) THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF (hour LT 10) THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF (minute LT 10) THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF (sec LT 10) THEN sec='0'+STRTRIM(FIX(sec),2) ELSE sec=STRTRIM(FIX(sec),2) end_date=STRTRIM(year,2)+month+day+'_'+hour+minute+sec out_filename='satplot__'+begin_date+'__'+end_date help,begin_date out_filename=out_filename+'__pa'+STRTRIM(SELF.position_angle,2)+'_d'+STRTRIM(SELF.delta,2)+'_'+STRMID(SELF.spacecraft,0,1,/REVERSE) print,out_filename RETURN,out_filename END FUNCTION tick_string, axis, index, value ;------------------------------------------------------- ; Plotter tick function to return correct string per tickmark. ;------------------------------------------------------- CALDAT,value,month,day,year,hour,minute,second ; Fix an odd problem where sometimes time is one minute early. minute = CEIL( minute ) IF ( minute GE 59 ) THEN BEGIN minute = 0 hour = hour + 1 ENDIF ; Pad the minute string with a zero, if necessary. IF ( minute LT 10 ) THEN minute = '0'+STRTRIM(minute,2) ELSE minute = STRTRIM(minute,2) IF ( hour EQ 0 ) THEN BEGIN ; Date. RETURN,STRTRIM(month,2)+'/'+STRTRIM(day,2)+'/'+STRTRIM(year,2) ENDIF ELSE BEGIN ; Time. RETURN,STRTRIM(hour,2)+':'+minute ENDELSE END PRO satplot::manage_widgets,event CASE event.ID OF SELF.tlb : BEGIN ;------------------------------------------------------- ; Resize by user. ;------------------------------------------------------- tlb_geom=WIDGET_INFO(SELF.tlb,/GEOMETRY) button_base0_geom=WIDGET_INFO(SELF.button_base0,/GEOMETRY) new_xsize=event.X-(tlb_geom.XPAD*2) new_ysize=event.Y-(tlb_geom.YPAD*2)-(button_base0_geom.YPAD)-(button_base0_geom.YSIZE) ; xsize minimum is 600 plus margins to allow for plot title. ysize minimum is 300 plus margins. new_xsize=new_xsize > (600+SELF.x1_margin+SELF.x2_margin) new_ysize=new_ysize > (300+SELF.y1_margin+SELF.y2_margin) WIDGET_CONTROL,SELF.widgetdraw,XSIZE=new_xsize,YSIZE=new_ysize SELF->redraw END SELF.widgetdraw : BEGIN WIDGET_CONTROL,/HOURGLASS ; Update the cursor readouts. ; x_ndx=ROUND((event.X-SELF.x1_margin-1)/SELF.zoom_value) ; y_ndx=ROUND((event.Y-SELF.y1_margin-1)/SELF.zoom_value) x_ndx=(event.X-SELF.x1_margin)/SELF.zoom_value y_ndx=(event.Y-SELF.y1_margin)/SELF.zoom_value IF (x_ndx LT 0) OR (x_ndx GE N_ELEMENTS(*SELF.time_ptr)) $ OR (y_ndx LT 0) OR (y_ndx GE N_ELEMENTS(*SELF.observed_angle_ptr)) THEN BEGIN ; Update elongation angle value. elongation_angle='n/a' elongation_angle=STRMID(SELF.elongation_angle_string, 0, STRLEN(SELF.elongation_angle_string)-STRLEN(elongation_angle)) + elongation_angle WIDGET_CONTROL,SELF.elongation_value_readout,SET_VALUE=elongation_angle ; Update time axis value. new_time='n/a' new_time=STRMID(SELF.time_axis_string, 0, STRLEN(SELF.time_axis_string)-STRLEN(new_time)) + new_time WIDGET_CONTROL,SELF.time_axis_value_readout,SET_VALUE=new_time ; ; Update date_obs value (only if srem files available, normally only at JPL). ; new_time='n/a' ; new_time=STRMID(SELF.date_obs_string, 0, STRLEN(SELF.date_obs_string)-STRLEN(new_time)) + new_time ; WIDGET_CONTROL,SELF.date_obs_value_readout,SET_VALUE=new_time ; Update detector value. detector='n/a' detector=STRMID(SELF.detector_string, 0, STRLEN(SELF.detector_string)-STRLEN(detector)) + detector WIDGET_CONTROL,SELF.detector_value_readout,SET_VALUE=detector RETURN ENDIF ;------------------------------------------------------------------------------------------------------- ; Kluge to force major tick marks to midnight instead of noon as per julian date convention. ; See matching (*SELF.time_ptr)-0.5d in redraw routine. ; Re-correct time forward by 12 hours. time=(*SELF.time_ptr)+0.5d ;------------------------------------------------------------------------------------------------------- ; Do a reverse-lookup to get the actual time for the jplot pixel (instead of just the 30-min cadence time.) CALDAT,time[x_ndx],month,day,year,hour,minute,sec year=STRTRIM(year,2) IF (month LT 10) THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF (day LT 10) THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF (hour LT 10) THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF (minute LT 10) THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF (sec LT 10) THEN sec='0'+STRTRIM(FIX(sec),2) ELSE sec=STRTRIM(FIX(sec),2) ; Spacecraft is 0 or 1 for GET_CLOSEST_SREM_FILENAME. sc=(STRMID(SELF.spacecraft,0,1,/REVERSE) EQ 'B') observed_angle=(*SELF.observed_angle_ptr)[y_ndx] tel=SELF->ANGLE2TEL(observed_angle) IF (STRLOWCASE(tel) EQ STRLOWCASE('cor1')) THEN BEGIN ; Update elongation angle value. elongation_angle='n/a' elongation_angle=STRMID(SELF.elongation_angle_string, 0, STRLEN(SELF.elongation_angle_string)-STRLEN(elongation_angle)) + elongation_angle WIDGET_CONTROL,SELF.elongation_value_readout,SET_VALUE=elongation_angle ; Update time axis value. new_time='n/a' new_time=STRMID(SELF.time_axis_string, 0, STRLEN(SELF.time_axis_string)-STRLEN(new_time)) + new_time WIDGET_CONTROL,SELF.time_axis_value_readout,SET_VALUE=new_time ; ; Update date_obs value (only if srem files available, normally only at JPL). ; date_obs='n/a' ; date_obs=STRMID(SELF.date_obs_string, 0, STRLEN(SELF.date_obs_string)-STRLEN(date_obs)) + date_obs ; WIDGET_CONTROL,SELF.date_obs_value_readout,SET_VALUE=date_obs ; Update detector value. detector='n/a' detector=STRMID(SELF.detector_string, 0, STRLEN(SELF.detector_string)-STRLEN(detector)) + detector WIDGET_CONTROL,SELF.detector_value_readout,SET_VALUE=detector RETURN ENDIF ; actual_filename=GET_CLOSEST_SREM_FILENAME(year+month+day,hour+minute+sec,sc,tel) ; actual_time=STRMID(FILE_BASENAME(actual_filename),0,15) ; actual_year=STRMID(actual_time,0,4) ; actual_month=STRMID(actual_time,4,2) ; actual_day=STRMID(actual_time,6,2) ; actual_hour=STRMID(actual_time,9,2) ; actual_minute=STRMID(actual_time,11,2) ; actual_sec=STRMID(actual_time,13,2) ; formatted_datetime=actual_year+'-'+actual_month+'-'+actual_day+'T'+actual_hour+':'+actual_minute+':'+actual_sec ; Update elongation angle. elongation_angle=STRMID(STRTRIM((*SELF.observed_angle_ptr)[y_ndx],2),0,5) elongation_angle=STRMID(SELF.elongation_angle_string, 0, STRLEN(SELF.elongation_angle_string)-STRLEN(elongation_angle)) + elongation_angle WIDGET_CONTROL,SELF.elongation_value_readout,SET_VALUE=elongation_angle ; Update time axis value. time_axis_ndx=FLOOR((event.X-SELF.x1_margin)/SELF.zoom_value) time_axis_value=time[time_axis_ndx] CALDAT,time_axis_value,month,day,year,hour,minute,sec IF (month LT 10) THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF (day LT 10) THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF (hour LT 10) THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF (minute LT 10) THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF (sec LT 10) THEN sec='0'+STRMID(STRTRIM(sec,2),0,5) ELSE sec=STRMID(STRTRIM(sec,2),0,6) datetime=STRTRIM(year,2)+'-'+month+'-'+day+'T'+hour+':'+minute+':'+sec time_axis_value=STRMID(datetime,0,19) time_axis_value=STRTRIM(time_axis_value,2) time_axis_value=STRMID(SELF.time_axis_string, 0, STRLEN(SELF.time_axis_string)-STRLEN(time_axis_value)) + time_axis_value WIDGET_CONTROL,SELF.time_axis_value_readout,SET_VALUE=time_axis_value ; ; Update date_obs value (only if srem files available, normally only at JPL). ; date_obs=formatted_datetime ; date_obs=STRMID(SELF.date_obs_string, 0, STRLEN(SELF.date_obs_string)-STRLEN(date_obs)) + date_obs ; WIDGET_CONTROL,SELF.date_obs_value_readout,SET_VALUE=date_obs ; Update detector. detector=tel detector=STRMID(SELF.detector_string, 0, STRLEN(SELF.detector_string)-STRLEN(detector)) + detector WIDGET_CONTROL,SELF.detector_value_readout,SET_VALUE=detector ;------------------------------------------------------- ; Remember the mouse button status. ;------------------------------------------------------- IF (event.TYPE EQ 0) AND (event.PRESS GE 1) THEN BEGIN SELF.mouse_button_is_down=event.PRESS ;------------------------------------------------------- ; Middle button is being pressed, switch TYPE to 2 so ; that it thinks motion is occuring ;------------------------------------------------------- IF (event.PRESS EQ 2) THEN event.TYPE=2 ENDIF IF (event.TYPE EQ 1) AND (event.RELEASE GE 2) THEN BEGIN SELF.mouse_button_is_down=0 ; Do redraw only on mouse release to speed up moving tiepoints. ;SELF->redraw ENDIF CASE 1 OF ;------------------------------------------------------- ; Add tiepoint on left mouse button press. ;------------------------------------------------------- (event.TYPE EQ 0 AND event.PRESS EQ 1 AND event.RELEASE EQ 0) : BEGIN WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 add_x_ndx=FLOOR((event.X-SELF.x1_margin)/SELF.zoom_value) add_y_ndx=FLOOR((event.Y-SELF.y1_margin)/SELF.zoom_value) ;add_x_ndx=x_ndx ;add_y_ndx=y_ndx ; This "time" variable has 0.5d added back in, see above. add_x_value=time[add_x_ndx] add_y_value=(*SELF.observed_angle_ptr)[add_y_ndx] add_x_pixel=add_x_ndx+SELF.x_start_value add_y_pixel=add_y_ndx+SELF.y_start_value ; Check to see if tiepoint already exists. SELF.chain=CHAIN_START_FINDER(SELF.chain) n_chain_links=CHAIN_LINK_COUNTER(SELF.chain) FOR link=0,n_chain_links-1 DO BEGIN data_string=(*(*SELF.chain).data)[0] data_string_elements=STRSPLIT(data_string,' ',/EXTRACT) tp_x_pixel=data_string_elements[N_ELEMENTS(data_string_elements)-2] tp_y_pixel=data_string_elements[N_ELEMENTS(data_string_elements)-1] ; Do nothing if tiepoint already exists for this pixel. IF ((tp_x_pixel EQ add_x_pixel) AND (tp_y_pixel EQ add_y_pixel)) THEN BEGIN WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 RETURN ENDIF IF PTR_VALID((*SELF.chain).next) THEN BEGIN SELF.chain=(*SELF.chain).next ENDIF ENDFOR ; Add tiepoint. ; Format the date value. ; Angle is used in the height column for this satplot application. observed_angle_degrees=STRMID(add_y_value,4,8) CALDAT,add_x_value,month,day,year,hour,minute,sec IF (month LT 10) THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF (day LT 10) THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF (hour LT 10) THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF (minute LT 10) THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF (sec LT 10) THEN sec='0'+STRMID(STRTRIM(sec,2),0,5) ELSE sec=STRMID(STRTRIM(sec,2),0,6) datetime=STRTRIM(year,2)+'-'+month+'-'+day+'T'+hour+':'+minute+':'+sec date=STRMID(datetime,0,19) ; Angle appears again but is formatted with one decimal. observed_angle_one_decimal=STRMID(ROUND(FLOAT(observed_angle_degrees)*10)/10.0,4,6) CASE (SELF->ANGLE2TEL(FLOAT(observed_angle_degrees))) OF 'hi_2' : tel=' HI2' 'hi_1' : tel=' HI1' 'cor2' : tel=' COR2' 'cor1' : tel=' COR1' ENDCASE fc=' 0' col=STRMID(FIX(add_x_pixel),3,5) row=STRMID(FIX(add_y_pixel),3,5) ; Do a reverse-lookup to get the actual time for the jplot pixel (instead of just the 30-min cadence time.) year=STRMID(date,0,4) month=STRMID(date,5,2) day=STRMID(date,8,2) hour=STRMID(date,11,2) minute=STRMID(date,14,2) sec=STRMID(date,17,2) ; ; Spacecraft is 0 or 1 for GET_CLOSEST_SREM_FILENAME. ; sc=([0,1])[(STRUPCASE(STRMID(SELF.spacecraft,0,1,/REVERSE)) EQ 'B')] ; tel=SELF->ANGLE2TEL(observed_angle_degrees) ; actual_filename=GET_CLOSEST_SREM_FILENAME(year+month+day,hour+minute+sec,sc,tel) ; actual_time_value=STRMID(FILE_BASENAME(actual_filename),0,15) ; year=STRMID(actual_time_value,0,4) ; month=STRMID(actual_time_value,4,2) ; day=STRMID(actual_time_value,6,2) ; hour=STRMID(actual_time_value,9,2) ; minute=STRMID(actual_time_value,11,2) ; sec=STRMID(actual_time_value,13,2) ; actual_time_string=year+'-'+month+'-'+day+'T'+hour+':'+minute+':'+sec ;DO TIME AXIS INSTEAD OF REVERSE-LOOUP. THIS ALLOWS FUNCTIONALITY AWAY FROM JPL ENVIRONMENT. time_axis_ndx=FLOOR((event.X-SELF.x1_margin)/SELF.zoom_value) time_axis_value=time[time_axis_ndx] CALDAT,time_axis_value,month,day,year,hour,minute,sec IF (month LT 10) THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF (day LT 10) THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF (hour LT 10) THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF (minute LT 10) THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF (sec LT 10) THEN sec='0'+STRMID(STRTRIM(sec,2),0,5) ELSE sec=STRMID(STRTRIM(sec,2),0,6) datetime=STRTRIM(year,2)+'-'+month+'-'+day+'T'+hour+':'+minute+':'+sec time_axis_value=STRMID(datetime,0,19) time_axis_value=STRTRIM(time_axis_value,2) data_string=observed_angle_degrees+' '+date+observed_angle_one_decimal+' '+tel+fc+col+row ; Add "actual time" as second element in data string array. ; data_string_array=[data_string,actual_time_string] data_string_array=[data_string,time_axis_value] SELF.chain=CHAIN_LINK_ADD(SELF.chain,data_string_array) SELF->redraw,/UPDATE_PLOT WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 END ;------------------------------------------------------- ; Delete tiepoint on right mouse button press. ; Remove it from the screen. ;------------------------------------------------------- ((event.TYPE EQ 0 AND event.PRESS EQ 4) AND PTR_VALID(SELF.chain)) : BEGIN del_x_ndx=FLOOR((event.X-SELF.x1_margin)/SELF.zoom_value) del_y_ndx=FLOOR((event.Y-SELF.y1_margin)/SELF.zoom_value) ;del_x_ndx=x_ndx ;del_y_ndx=y_ndx del_x_pixel=del_x_ndx+SELF.x_start_value del_y_pixel=del_y_ndx+SELF.y_start_value SELF.chain=CHAIN_START_FINDER(SELF.chain) n_chain_links=CHAIN_LINK_COUNTER(SELF.chain) ; First, check all points for for direct hit. FOR link=0,n_chain_links-1 DO BEGIN data_string=(*(*SELF.chain).data)[0] data_string_elements=STRSPLIT(data_string,' ',/EXTRACT) tp_x_pixel=data_string_elements[N_ELEMENTS(data_string_elements)-2] tp_y_pixel=data_string_elements[N_ELEMENTS(data_string_elements)-1] ok_to_delete_tp=0 CASE 1 OF ; If direct hit then delete point. ((tp_x_pixel EQ del_x_pixel) AND (tp_y_pixel EQ del_y_pixel)) : BEGIN ok_to_delete_tp=1 END ; Else do nothing. ELSE : BEGIN END ENDCASE IF (ok_to_delete_tp EQ 1) THEN BEGIN SELF.chain=CHAIN_LINK_DELETE(SELF.chain) WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 SELF->redraw,/UPDATE_PLOT WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 RETURN ENDIF IF PTR_VALID((*SELF.chain).next) THEN BEGIN SELF.chain=(*SELF.chain).next ENDIF ENDFOR SELF.chain=CHAIN_START_FINDER(SELF.chain) ; Secondly, check for nearness. FOR link=0,n_chain_links-1 DO BEGIN data_string=(*(*SELF.chain).data)[0] data_string_elements=STRSPLIT(data_string,' ',/EXTRACT) tp_x_pixel=data_string_elements[N_ELEMENTS(data_string_elements)-2] tp_y_pixel=data_string_elements[N_ELEMENTS(data_string_elements)-1] ok_to_delete_tp=0 ; Escalating reach, so as to avoid multiple unwanted deletions. CASE 1 OF ; If neighbor on same line then delete point. ((ABS(tp_x_pixel-del_x_pixel) EQ 1) AND (tp_y_pixel EQ del_y_pixel)) : BEGIN ok_to_delete_tp=1 END ; If neighbor within one pixel in any direction then delete point. (SQRT((tp_x_pixel-del_x_pixel)^2+(tp_y_pixel-del_y_pixel)^2) LE SQRT(2.0d)) : BEGIN ok_to_delete_tp=1 END ; Else do nothing. ELSE : BEGIN END ENDCASE IF (ok_to_delete_tp EQ 1) THEN BEGIN SELF.chain=CHAIN_LINK_DELETE(SELF.chain) WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 SELF->redraw,/UPDATE_PLOT WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 RETURN ENDIF IF PTR_VALID((*SELF.chain).next) THEN BEGIN SELF.chain=(*SELF.chain).next ENDIF ENDFOR END ELSE : BEGIN END ENDCASE END SELF.save_picture_button : BEGIN WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 curwin=!D.WINDOW WSET,SELF.draw_window_id img2sav=TVRD() png_filename=SELF->get_output_filename()+'_picture.png' WRITE_PNG,png_filename,img2sav PRINT,'Picture saved as: ',png_filename tv,img2sav*0.75 FOR thick=10,1,-1 DO XYOUTS,0.5,0.6,'picture',CHARSIZE=10,CHARTHICK=thick,COLOR=255/thick,/NORMAL,ALIGN=0.5 FOR thick=10,1,-1 DO XYOUTS,0.5,0.3,'saved',CHARSIZE=10,CHARTHICK=thick,COLOR=255/thick,/NORMAL,ALIGN=0.5 wait,1 SELF->redraw WSET,curwin WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 END SELF.save_plot_button : BEGIN WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 curwin=!D.WINDOW ; Use WINDOW instead of WSET so that RETAIN=2 can be invoked, which is critical for plotting. WINDOW,SELF.plot_window_id,RETAIN=2 SELF->redraw,/UPDATE_PLOT WSET,SELF.plot_window_id img2sav=TVRD() png_filename=SELF->get_output_filename()+'_plot.png' WRITE_PNG,png_filename,img2sav PRINT,'Plot saved as: ',png_filename tv,img2sav*0.75 FOR thick=10,1,-1 DO XYOUTS,0.5,0.6,'plot',CHARSIZE=10,CHARTHICK=thick,COLOR=255/thick,/NORMAL,ALIGN=0.5 FOR thick=10,1,-1 DO XYOUTS,0.5,0.3,'saved',CHARSIZE=10,CHARTHICK=thick,COLOR=255/thick,/NORMAL,ALIGN=0.5 wait,1 SELF->redraw,/UPDATE_PLOT WSET,curwin WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 END SELF.save_report_button : BEGIN WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 SELF.chain=CHAIN_START_FINDER(SELF.chain) n_chain_links=CHAIN_LINK_COUNTER(SELF.chain) all_data=STRARR(n_chain_links) ; Extract all data from chain. FOR link=0,n_chain_links-1 DO BEGIN actual_time_string=(*(*SELF.chain).data)[1] data_string=(*(*SELF.chain).data)[0] all_data[link]=data_string IF (PTR_VALID((*SELF.chain).next) EQ 1) THEN BEGIN SELF.chain=(*SELF.chain).next ENDIF data=all_data[link] ; Skip over all leading whitespaces in the data string. position=0 WHILE (STRMID(data,position,1) EQ ' ') DO BEGIN position=position+1 ENDWHILE ; Locate the whitespaces surrounding the time string. first_whitespace=STRPOS(data,' ',position+1) second_whitespace=STRPOS(data,' ',first_whitespace+1) all_data[link]=STRMID(data,0,first_whitespace+1)+actual_time_string+STRMID(data,second_whitespace,STRLEN(data)) ENDFOR ; Sort data by observed angle. all_angles=FLOAT(STRMID(all_data,0,8)) sort_ndx=SORT(all_angles) all_data=all_data[sort_ndx] all_angles=all_angles[sort_ndx] ; Sub-sort by time, if necessary. IF (N_ELEMENTS(UNIQ(all_angles)) LT N_ELEMENTS(all_angles)) THEN BEGIN FOR angle=0,N_ELEMENTS(all_angles)-1 DO BEGIN sort_completed=0 duplicate_angle_ndx=WHERE(all_angles EQ all_angles[angle],duplicate_angle_count) IF ((duplicate_angle_count GT 1) AND (sort_completed EQ 0)) THEN BEGIN ; Collect up all the julian times from the duplicate angle data strings. time2sort=DBLARR(duplicate_angle_count) FOR dup_ang=0,duplicate_angle_count-1 DO BEGIN ; Skip over all leading whitespaces in the data string. time_position=0 WHILE (STRMID(all_data[duplicate_angle_ndx[dup_ang]],time_position,1) EQ ' ') DO BEGIN time_position=time_position+1 ENDWHILE time_position=STRPOS(data,' ',time_position+1)+1 ; Convert time tag string to julian. time_tag_string=STRMID(all_data[duplicate_angle_ndx[dup_ang]],time_position,STRLEN('yyyy-mm-ddThh:mm:ss')) jd_struct=ANYTIM2JD(time_tag_string) time2sort[dup_ang]=jd_struct.(0)+jd_struct.(1) ENDFOR all_data[duplicate_angle_ndx]=all_data[duplicate_angle_ndx[SORT(time2sort)]] ENDIF ENDFOR ENDIF OPENW,lun,SELF->get_output_filename()+'.ht',/GET_LUN PRINTF,lun,'#VERSION=5' PRINTF,lun,'#DATE-OBS: '+STRMID((STRSPLIT(all_data[0],' ',/EXTRACT))[1],0,10) PRINTF,lun,'#TIME-OBS: '+STRMID((STRSPLIT(all_data[0],' ',/EXTRACT))[1],11,12) PRINTF,lun,'#SPACECRAFT: '+STRUPCASE(STRMID(STRTRIM(SELF.spacecraft,2),0,1,/REVERSE)) PRINTF,lun,'#DETECTOR: JMAP' PRINTF,lun,'#FILTER:' PRINTF,lun,'#POLAR:' PRINTF,lun,'#OBSERVER: pliewer' PRINTF,lun,'#IMAGE_TYPE:' PRINTF,lun,'#UNITS: Degrees' PRINTF,lun,'#PLATESCALE:' PRINTF,lun,'#SUBFIELD[0,0]:' PRINTF,lun,'#COMMENT:' PRINTF,lun,'#FEAT_CODE:' PRINTF,lun,'# HEIGHT DATE TIME ANGLE TEL FC COL ROW' FOR data=0,n_chain_links-1 DO BEGIN PRINTF,lun,all_data[data] ENDFOR FREE_LUN,lun help,SELF->get_output_filename() PRINT,'Report saved as: ',SELF->get_output_filename()+'.ht' WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 END SELF.plot_button_on : BEGIN WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 SELF.actual_time_plot=1 ; Use WINDOW instead of WSET so that RETAIN=2 can be invoked, which is critical for plotting. WINDOW,SELF.plot_window_id,RETAIN=2 ERASE,!D.N_COLORS-1 SELF.plot_window_is_onscreen=1 IF (CHAIN_LINK_COUNTER(SELF.chain) GT 0) THEN BEGIN WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 SELF->plot_all_tiepoints,/UPDATE_PLOT WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 WIDGET_CONTROL,SELF.save_plot_button,SENSITIVE=1 ENDIF ELSE BEGIN WIDGET_CONTROL,SELF.save_plot_button,SENSITIVE=0 XYOUTS,0.5,0.5,'NO TIEPOINTS',ALIGN=0.5,CHARSIZE=5,COLOR=0,/NORMAL ENDELSE WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 END SELF.plot_button_off : BEGIN WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 IF (SELF.plot_window_id GE 0) THEN BEGIN IF (SELF.plot_window_is_onscreen EQ 1) THEN BEGIN WDELETE,SELF.plot_window_id WIDGET_CONTROL,SELF.save_plot_button,SENSITIVE=0 SELF.plot_window_is_onscreen=0 ENDIF ENDIF SELF.actual_time_plot=0 WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 END SELF.read_report_button : BEGIN ; Read existing points file that was previously saved using this program. IF CHAIN_LINK_COUNTER(SELF.chain) GT 0 THEN BEGIN answer=DIALOG_MESSAGE('Importing points from HT file will replace existing tiepoints. Continue Reading?',/QUESTION) If answer EQ 'No' THEN BEGIN RETURN ENDIF ENDIF ht_filename=DIALOG_PICKFILE() IF ht_filename EQ '' THEN BEGIN ; Indicates "Cancel" was clicked in the DIALOG_PICKFILE dialog. RETURN ENDIF ;-------------------------------------------------------------------------------------------------------------------------------------- ; ********** BEGIN ERROR CHECKS ********** ; ; Error check the filename convention. IF STRLEN(FILE_BASENAME(ht_filename)) LT 4 THEN BEGIN ddddd=DIALOG_MESSAGE(['Filename must conform to this convention.', $ 'Example:', $ 'satplot__20110607_000000__20110615_000000__pa252_d5_B.ht']) RETURN ENDIF IF N_ELEMENTS(STRSPLIT(FILE_BASENAME(ht_filename),'_')) NE 8 THEN BEGIN ddddd=DIALOG_MESSAGE(['Filename must conform to this convention.', $ 'Example:', $ 'satplot__20110607_000000__20110615_000000__pa252_d5_B.ht']) RETURN ENDIF IF STRMID(FILE_BASENAME(ht_filename),2,3,/REVERSE) NE '.ht' THEN BEGIN ddddd=DIALOG_MESSAGE(['Filename must have .ht extension.', $ 'Example:', $ 'satplot__20110607_000000__20110615_000000__pa252_d5_B.ht']) RETURN ENDIF ; Error check the start time. Must conform to previously initialized value. datetime_first=(STRSPLIT(FILE_BASENAME(ht_filename),'_',/EXTRACT))[1]+'_'+(STRSPLIT(FILE_BASENAME(ht_filename),'_',/EXTRACT))[2] help,datetime_first jd_structure_first=ANYTIM2JD( datetime_first ) jd_first=jd_structure_first.(0) + jd_structure_first.(1) IF (jd_first NE SELF.jd_first) THEN BEGIN CALDAT,SELF.jd_first,month,day,year,hour,minute,sec year=STRTRIM(year,2) IF month LT 10 THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF day LT 10 THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF hour LT 10 THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF minute LT 10 THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF sec LT 10 THEN sec='0'+STRTRIM(sec,2) ELSE sec=STRTRIM(sec,2) datetime=STRTRIM(year,2)+'-'+month+'-'+day+'_'+hour+':'+minute+':'+sec ddddd=DIALOG_MESSAGE(['Incompatable HT file.', $ 'Start Time must be the same as initialized.', $ 'Initialized value: '+datetime, $ 'Incompatable value: '+datetime_first, $ '', $ 'Failed to import: '+ht_filename],/INFORMATION) RETURN ENDIF ; Error check the end time. Must conform to previously initialized value. datetime_last=(STRSPLIT(FILE_BASENAME(ht_filename),'_',/EXTRACT))[3]+'_'+(STRSPLIT(FILE_BASENAME(ht_filename),'_',/EXTRACT))[4] jd_structure_last=ANYTIM2JD( datetime_last ) jd_last=jd_structure_last.(0) + jd_structure_last.(1) IF (jd_last NE SELF.jd_last) THEN BEGIN CALDAT,SELF.jd_last,month,day,year,hour,minute,sec year=STRTRIM(year,2) IF month LT 10 THEN month='0'+STRTRIM(month,2) ELSE month=STRTRIM(month,2) IF day LT 10 THEN day='0'+STRTRIM(day,2) ELSE day=STRTRIM(day,2) IF hour LT 10 THEN hour='0'+STRTRIM(hour,2) ELSE hour=STRTRIM(hour,2) IF minute LT 10 THEN minute='0'+STRTRIM(minute,2) ELSE minute=STRTRIM(minute,2) IF sec LT 10 THEN sec='0'+STRTRIM(sec,2) ELSE sec=STRTRIM(sec,2) datetime=STRTRIM(year,2)+'-'+month+'-'+day+'_'+hour+':'+minute+':'+sec ddddd=DIALOG_MESSAGE(['Incompatable HT file.', $ 'Ending Time must be the same as initialized.', $ 'Initialized value: '+datetime, $ 'Incompatable value: '+datetime_last, $ '', $ 'Failed to import: '+ht_filename],/INFORMATION) RETURN ENDIF ; Error check the position angle. Must conform to previously initialized value. position_angle=(STRSPLIT(FILE_BASENAME(ht_filename),'_',/EXTRACT))[5] position_angle=FLOAT(STRMID(position_angle,2,STRLEN(position_angle)-2)) IF (position_angle NE SELF.position_angle) THEN BEGIN ddddd=DIALOG_MESSAGE(['Incompatable HT file.', $ 'Position Angle must be the same as initialized.', $ 'Initialized value: '+STRTRIM(SELF.position_angle,2), $ 'Incompatable value: '+STRTRIM(position_angle,2), $ '', $ 'Failed to import: '+ht_filename],/INFORMATION) RETURN ENDIF ; Error check the delta. Must conform to previously initialized value. delta=(STRSPLIT(FILE_BASENAME(ht_filename),'_',/EXTRACT))[6] delta=FLOAT(STRMID(delta,1,STRLEN(delta)-1)) IF (delta NE SELF.delta) THEN BEGIN ddddd=DIALOG_MESSAGE(['Incompatable HT file.', $ 'Delta value must be the same as initialized.', $ 'Initialized value: '+STRTRIM(SELF.delta,2), $ 'Incompatable value: '+STRTRIM(delta,2), $ '', $ 'Failed to import: '+ht_filename],/INFORMATION) RETURN ENDIF ; Error check the spacecraft. Must conform to previously initialized value. spacecraft='STEREO-'+STRUPCASE((STRSPLIT((STRSPLIT(FILE_BASENAME(ht_filename),'_',/EXTRACT))[7],'.',/EXTRACT))[0]) IF (spacecraft NE SELF.spacecraft) THEN BEGIN ddddd=DIALOG_MESSAGE(['Incompatable HT file.', $ 'Spacecraft must be the same as initialized.', $ 'Initialized value: '+SELF.spacecraft, $ 'Incompatable value: '+spacecraft, $ '', $ 'Failed to import: '+ht_filename], $ /INFORMATION) RETURN ENDIF ; ; ********** FINISHED ERROR CHECKS ********** ;-------------------------------------------------------------------------------------------------------------------------------------- ; Eliminate any existing tiepoints. IF PTR_VALID(SELF.chain) THEN BEGIN result=CHAIN_DEATH(SELF.chain) ENDIF ; Read the selected file. ascii=READ_ASCII(ht_filename,COUNT=count) ascii=STRARR(count) OPENR,lun,ht_filename,/GET_LUN READF,lun,ascii FREE_LUN,lun ; Isolate data from metadata. ndx=WHERE(STRMID(ascii,0,1) NE '#',count) all_data=ascii[ndx] ; Populate the chain with tiepoints from the file. FOR t=0,count-1 DO BEGIN data=all_data[t] ; Skip over all leading whitespaces in the data string. position=0 WHILE (STRMID(data,position,1) EQ ' ') DO BEGIN position=position+1 ENDWHILE ; Locate the whitespaces surrounding the time string. first_whitespace=STRPOS(data,' ',position+1) second_whitespace=STRPOS(data,' ',first_whitespace+1) actual_time_value=STRMID(data,first_whitespace+1,second_whitespace-first_whitespace) ; Create one link with one tiepoint. SELF.chain=CHAIN_LINK_ADD(SELF.chain,[data,actual_time_value]) ENDFOR ; Redraw using new tiepoints. SELF->redraw,/UPDATE_PLOT END SELF.curvefit_button : BEGIN SELF->manage_widgets,{id:SELF.save_report_button,top:SELF.tlb,handler:SELF.tlb,select:1} curwin=!D.WINDOW ; Curve fit program by Chris Mostl, Mar 2012, produces two fit methods: Fixed Phi and Harmonic Mean. WIDGET_CONTROL,/HOURGLASS results=fitall(SELF->get_output_filename()+'.ht') WSET,curwin END ELSE: ENDCASE END PRO manage_widgets,event ;------------------------------------------------------- ; This routine is necessary because widget event handler ; routines cannot be object methods. This routine just ; passes the event to an object method. ;------------------------------------------------------- WIDGET_CONTROL,event.TOP,GET_UVALUE=satplot_obj IF OBJ_VALID(satplot_obj) THEN satplot_obj->manage_widgets,event END PRO satplot::create_widgets SELF.tlb=WIDGET_BASE(/TLB_SIZE_EVENTS,TITLE='Solar Angle-Time Plot',UVALUE=SELF,KILL_NOTIFY='cleanup_pro',/COLUMN) ; xsize and ysize defaults are lesser of image size or 80% screen size. draw_xsize=((GET_SCREEN_SIZE())[0]*.8) < (SELF.x_dataelements+SELF.x1_margin+SELF.x2_margin) draw_ysize=((GET_SCREEN_SIZE())[1]*.8) < (SELF.y_dataelements+SELF.y1_margin+SELF.y2_margin) ; xsize minimum is 600 plus margins to allow for plot title. ysize minimum is 300 plus margins. draw_xsize=(600+SELF.x1_margin+SELF.x2_margin) > draw_xsize draw_ysize=(300+SELF.y1_margin+SELF.y2_margin) > draw_ysize draw_xsize=ROUND(draw_xsize) draw_ysize=ROUND(draw_ysize) ; ysize default is lesser of image height or 90% screen height. SELF.button_base0=WIDGET_BASE(SELF.tlb,/ROW) button_base2=WIDGET_BASE(SELF.button_base0,/COLUMN) ; SELF.up_button=WIDGET_BUTTON(SELF.button_base1,VALUE='^') ; SELF.down_button=WIDGET_BUTTON(SELF.button_base1,VALUE='v') x_start_label=WIDGET_LABEL(button_base2,VALUE='Time Start') SELF.x_start_slider=WIDGET_SLIDER(button_base2,/SUPPRESS_VALUE,EVENT_PRO='x_start_slider_eh',$ MIN=0,MAX=SELF.x_start_slider_max,/DRAG) SELF.zoom_label=WIDGET_LABEL(button_base2,VALUE='Zoom '+STRMID(STRTRIM(SELF.zoom_value,2),0,3)) SELF.zoom_slider=WIDGET_SLIDER(button_base2,/SUPPRESS_VALUE,EVENT_PRO='zoom_slider_eh',$ MIN=ROUND(1/SELF.zoom_scale),MAX=SELF.zoom_max,/DRAG) button_base3=WIDGET_BASE(SELF.button_base0,/COLUMN) y_start_label=WIDGET_LABEL(button_base3,VALUE='Angle Start') SELF.y_start_label=WIDGET_LABEL(button_base3,VALUE=' 0.000 ') SELF.y_start_slider=WIDGET_SLIDER(SELF.button_base0,/SUPPRESS_VALUE,EVENT_PRO='y_start_slider_eh',$ MIN=0,MAX=SELF.y_start_slider_max,/DRAG,/VERTICAL) button_base4=WIDGET_BASE(SELF.button_base0,/COLUMN) button_base4a=WIDGET_BASE(button_base4,/COLUMN,/FRAME) SELF.point_count_string= 'Points = 0' SELF.elongation_angle_string='Elongation = n/a' ; SELF.date_obs_string= 'Date_Obs = n/a' SELF.time_axis_string= 'Time Axis = n/a' SELF.detector_string= 'Detector = n/a' SELF.tiepoint_count_box=WIDGET_LABEL(button_base4a,VALUE=SELF.point_count_string) button_base4b=WIDGET_BASE(button_base4,/COLUMN,/FRAME) label=WIDGET_LABEL(button_base4b,VALUE='Cursor Coordinates') SELF.elongation_value_readout=WIDGET_LABEL(button_base4b,VALUE=SELF.elongation_angle_string) SELF.time_axis_value_readout=WIDGET_LABEL(button_base4b,VALUE=SELF.time_axis_string) ; SELF.date_obs_value_readout=WIDGET_LABEL(button_base4b,VALUE=SELF.date_obs_string) SELF.detector_value_readout=WIDGET_LABEL(button_base4b,VALUE=SELF.detector_string) button_base5=WIDGET_BASE(SELF.button_base0,/COLUMN,/FRAME) save_label=WIDGET_LABEL(button_base5,VALUE='Export') SELF.save_report_button=WIDGET_BUTTON(button_base5,VALUE='Points') SELF.save_picture_button=WIDGET_BUTTON(button_base5,VALUE='Picture') SELF.save_plot_button=WIDGET_BUTTON(button_base5,VALUE='Plot') button_base6=WIDGET_BASE(SELF.button_base0,/COLUMN) button_base6a=WIDGET_BASE(button_base6,/COLUMN,/FRAME) save_label=WIDGET_LABEL(button_base6a,VALUE='Import') SELF.read_report_button=WIDGET_BUTTON(button_base6a,VALUE='Points (.ht)') button_base6b=WIDGET_BASE(button_base6,/COLUMN,/FRAME) ; plot_label=WIDGET_LABEL(button_base6b,VALUE='Plot') button_base7=WIDGET_BASE(button_base6b,/COLUMN,/EXCLUSIVE) SELF.plot_button_on=WIDGET_BUTTON(button_base7,VALUE='Plot On') SELF.plot_button_off=WIDGET_BUTTON(button_base7,VALUE='Plot Off') button_base8=WIDGET_BASE(SELF.button_base0,/COLUMN,/FRAME) SELF.min_tiepoints_for_curvefit=4 SELF.curvefit_label=WIDGET_LABEL(button_base8,VALUE='('+STRTRIM(SELF.min_tiepoints_for_curvefit,2)+' pt min)') SELF.curvefit_button=WIDGET_BUTTON(button_base8,VALUE='Curve Fit',SENSITIVE=0) curvefit_label=WIDGET_LABEL(button_base8,VALUE='Points') curvefit_label=WIDGET_LABEL(button_base8,VALUE='will be') curvefit_label=WIDGET_LABEL(button_base8,VALUE='exported') curvefit_label=WIDGET_LABEL(button_base8,VALUE='to file.') SELF.widgetdraw=WIDGET_DRAW(SELF.tlb,XSIZE=draw_xsize,YSIZE=draw_ysize,RETAIN=2,/BUTTON_EVENTS,/MOTION_EVENTS) WIDGET_CONTROL,SELF.tlb,/REALIZE WIDGET_CONTROL,SELF.widgetdraw,GET_VALUE=window_id SELF.draw_window_id=window_id curwin=!D.WINDOW WINDOW ERASE,!D.N_COLORS-1 SELF.plot_window_id=!D.WINDOW WSET,curwin END PRO satplot::cleanup PTR_FREE,SELF.jplot_image_ptr PTR_FREE,SELF.observed_angle_master_ptr PTR_FREE,SELF.observed_angle_ptr PTR_FREE,SELF.time_master_ptr PTR_FREE,SELF.time_ptr IF PTR_VALID(SELF.chain) THEN BEGIN result=CHAIN_DEATH(SELF.chain) ENDIF IF (SELF.plot_window_is_onscreen EQ 1 AND !D.WINDOW GE 0) THEN BEGIN ;print,'SELF.plot_window_id = ',SELF.plot_window_id ;print,'SELF.plot_window_is_onscreen = ',SELF.plot_window_is_onscreen ;print,'!d.window = ',!d.window WDELETE,SELF.plot_window_id ENDIF END PRO cleanup_pro,tlb WIDGET_CONTROL,tlb,GET_UVALUE=satplot OBJ_DESTROY,satplot END FUNCTION satplot::init,jplot_image,datetime_first,datetime_last,position_angle,delta,spacecraft SELF.jplot_image_ptr=PTR_NEW(jplot_image) SELF.x_dataelements=N_ELEMENTS((*SELF.jplot_image_ptr)[*,0]) SELF.y_dataelements=N_ELEMENTS((*SELF.jplot_image_ptr)[0,*]) jd_structure_first=ANYTIM2JD( datetime_first ) jd_structure_last=ANYTIM2JD( datetime_last ) SELF.jd_first=jd_structure_first.(0) + jd_structure_first.(1) SELF.jd_last=jd_structure_last.(0) + jd_structure_last.(1) time_master=(DINDGEN(SELF.x_dataelements)/(SELF.x_dataelements-1))*(SELF.jd_last-SELF.jd_first)+SELF.jd_first SELF.time_master_ptr=PTR_NEW(time_master) ; Build observed angle. SELF.observed_angle_cadence=(90.0)/(SELF.y_dataelements-1) SELF.observed_angle_master_ptr=PTR_NEW(DBLARR((SELF.y_dataelements))) FOR t=1,SELF.y_dataelements-1 DO BEGIN (*SELF.observed_angle_master_ptr)[t]=(*SELF.observed_angle_master_ptr)[t-1]+SELF.observed_angle_cadence ENDFOR ; Calculate cadence (fraction of angle degree per row and fraction of julian day per image column). ; (The jmaps are constant degrees per pixel [Plate Carree], thus per-pixel-cadence is a fixed value.) SELF.jd_cadence=(SELF.jd_last-SELF.jd_first)/(SELF.x_dataelements-1) SELF.position_angle=position_angle SELF.delta=delta SELF.spacecraft=spacecraft SELF.x_start_slider_max=100 SELF.y_start_slider_max=80 SELF.zoom_scale=0.1d SELF.zoom_value=1.0d SELF.zoom_max=100 SELF.x1_margin=80 SELF.y1_margin=80 SELF.x2_margin=80 SELF.y2_margin=80 CASE STRMID(STRUPCASE(SELF.spacecraft),0,1,/REVERSE) OF 'A' : BEGIN SELF.cor2_min_angle=0.5 ;0.83 SELF.hi_1_min_angle=3.75 SELF.hi_2_min_angle=18.1 END 'B' : BEGIN SELF.cor2_min_angle=0.5 ;0.95 SELF.hi_1_min_angle=4.10 SELF.hi_2_min_angle=19.0 END ENDCASE SELF->create_widgets WIDGET_CONTROL,SELF.plot_button_on,/SET_BUTTON SELF.actual_time_plot=1 SELF.plot_window_is_onscreen=1 SELF->calculate_everything XMANAGER,'satplot',SELF.tlb,EVENT_HANDLER='manage_widgets',NO_BLOCK=1 WIDGET_CONTROL,SELF.tlb,SENSITIVE=0 SELF->redraw,/UPDATE_PLOT WIDGET_CONTROL,SELF.tlb,SENSITIVE=1 RETURN,1 END PRO satplot__define satplot={ $ satplot,$ tlb:0L,$ button_base0:0L,$ ; up_button:0L,$ button_base1:0L,$ point_count_string:'',$ elongation_angle_string:'',$ time_axis_string:'',$ ; date_obs_string:'',$ detector_string:'',$ tiepoint_count_box:0L,$ ; down_button:0L,$ widgetdraw:0L,$ draw_window_id:0L,$ plot_window_id:0L,$ plot_window_is_onscreen:0,$ x1_position:0.0d,$ y1_position:0.0d,$ x2_position:0.0d,$ y2_position:0.0d,$ x1_margin:0.0d,$ y1_margin:0.0d,$ x2_margin:0.0d,$ y2_margin:0.0d,$ x_plotspace:0L,$ y_plotspace:0L,$ plot_x_pixels:0L,$ plot_y_pixels:0L,$ formatted_datetime:'',$ jd_first:0.0d,$ jd_last:0.0d,$ jd_cadence:0.0d,$ observed_angle_cadence:0.0d,$ position_angle:'',$ delta:'',$ spacecraft:'',$ zoom_value:0.0d,$ zoom_slider:0L,$ zoom_label:0L,$ zoom_max:0.0d,$ zoom_scale:0.0d,$ x_start_slider_max:0L,$ x_start_value:0L,$ x_start_slider:0L,$ y_start_slider_max:0L,$ y_start_value:0L,$ y_end_value:0L,$ y_start_slider:0L,$ y_start_label:0L,$ time_axis_value_readout:0L,$ ; date_obs_value_readout:0L,$ elongation_value_readout:0L,$ detector_value_readout:0L,$ save_picture_button:0L,$ save_plot_button:0L,$ save_report_button:0L,$ read_report_button:0L,$ actual_time_plot_button:0L,$ plot_button_on:0L,$ plot_button_off:0L,$ min_tiepoints_for_curvefit:0L,$ curvefit_button:0L,$ curvefit_label:0L,$ actual_time_plot:0L,$ subset_xs:0L,$ subset_xe:0L,$ subset_ys:0L,$ subset_ye:0L,$ xmin:0.0d,$ xmax:0.0d,$ ymin:0.0d,$ ymax:0.0d,$ x_dataelements:0L,$ y_dataelements:0L,$ cor2_min_angle:0.0,$ hi_1_min_angle:0.0,$ hi_2_min_angle:0.0,$ mouse_button_is_down:0L,$ chain:PTR_NEW(),$ time_master_ptr:PTR_NEW(),$ time_ptr:PTR_NEW(),$ observed_angle_master_ptr:PTR_NEW(),$ observed_angle_ptr:PTR_NEW(),$ jplot_image_ptr:PTR_NEW()} END PRO satplot,jplot_image_filename datetime_first=(STRSPLIT(jplot_image_filename,'_',/EXTRACT))[1]+'_'+(STRSPLIT(jplot_image_filename,'_',/EXTRACT))[2] help,datetime_first datetime_last=(STRSPLIT(jplot_image_filename,'_',/EXTRACT))[3]+'_'+(STRSPLIT(jplot_image_filename,'_',/EXTRACT))[4] position_angle=FLOAT((STRSPLIT(jplot_image_filename,'_',/EXTRACT))[5]) delta=FLOAT((STRSPLIT(jplot_image_filename,'_',/EXTRACT))[6]) WHILE (STRMID(position_angle,0,1,/REVERSE) EQ '0') DO position_angle=STRMID(position_angle,0,STRLEN(position_angle)-1) IF (STRMID(position_angle,0,1,/REVERSE) EQ '.') THEN position_angle=STRMID(position_angle,0,STRLEN(position_angle)-1) WHILE (STRMID(delta,0,1,/REVERSE) EQ '0') DO delta=STRMID(delta,0,STRLEN(delta)-1) IF (STRMID(delta,0,1,/REVERSE) EQ '.') THEN delta=STRMID(delta,0,STRLEN(delta)-1) spacecraft='STEREO-'+STRUPCASE((STRSPLIT((STRSPLIT(jplot_image_filename,'_',/EXTRACT))[7],'.',/EXTRACT))[0]) satplot_obj=OBJ_NEW('satplot',READ_IMAGE(jplot_image_filename),datetime_first,datetime_last,position_angle,delta,spacecraft) END