#!/usr/bin/wish -f ## ## SCRIPT: tkReadOutlineFile_drawOutlineOnCanvas.tk ## ## PURPOSE: This Tk GUI script reads a file whose lines contain pairs of ## coordinates (example: longitude and latitude in decimal degrees) ## and plots those points in a rectangular Tk canvas area --- by ## connecting each point to the previous point using a ## straight line segment. ## ## The GUI allows several options to the user, including: ## ## - an entry field for a 'filter-precision' number --- to ## allow for skipping any point that is very close ## to the previous point ## ## - three buttons by which to call an external color-selector ## GUI by which to specify ## - a background color (for the canvas) ## - a color for the line segments, and ## - a color for any text that may be put on the canvas. ## ## NOTE: The outline data does not have to be map data. ## It could be pairs of coordinates that provide an ## outline of almost any object --- for example a ## person or an animal or an insect or a fish or ## a logo or alphanumeric characters in different fonts ## or geometric figures (like pentagons, hexagons, ...) ## or mosaic patterns or whatever. ## ##+################ ## READING THE DATA (and sources of data) : ##+################ ## ## The x,y (e.g. longitude,latitude) data in the input file is assumed ## to be in ASCII format, NOT binary. ## ## There are many country/continent/state boundary/outline data files ## on the internet in *ASCII* format. Examples: ## - GeoJSON files ## References: ## https://en.wikipedia.org/wiki/JSON ## https://en.wikipedia.org/wiki/GeoJSON ## geojson.org ## json.org ## - KML and KMZ files (where KML = Keyhole Markup Language) ## References: ## https://en.wikipedia.org/wiki/Keyhole_Markup_Language ## http://www.opengeospatial.org/standards/kml/ ## https://developers.google.com/kml/documentation/?csw=1 ## ## There is a lot of XML-like extra markup language in '.geojson' ## and '.kml'/'.kmz' files. ## ## For input to this utility, we take those files and 'clean them up' ## so that there are only a pair of space-separated (or comma-separated) ## decimal numbers in each line --- and a few comment lines indicated by ## '#' in column one of each comment line. ## ## This utility reads input files in this very simple, minimal format. ## Thus, we avoid the need to add to this utility the ability to read ## '.geojson' and '.kml' (and '.kmz') files --- which would require ## very elaborate parsing logic in order to handle the many ways in ## which people create these KML and JSON files. ## ## (Note: We include in the file-reading proc the ability to ## replace any comma character with a space --- so that this ## utility can read CSV = Comma-Separated-Variable files ## without having to edit the file to remove the commas.) ## ## In reading the file, the min,max x and y values are determined ## and a margin factor is applied to give an initial rectangle ## (in 'world coordinates') in which to initially plot the data. ## ##+############# ## GUI FEATURES: ## ## The GUI is to allow the user to ## - enter or get a filename ## - (re)read the file ## - specify a 'filter-precision' value ## - specify an image size (width in pixels) ## - specify min,max of x,y values for the plot area ## - select 3 colors (for background, lines, text). ## ## (We may provide a Write-Postscript-File option/button. ## But the user can also use a screen-capture utility, ## such as 'gnome-screenshot' on Linux, to capture an ## image of any outline that is drawn.) ## ##+############## ## THE GUI LAYOUT: ## ## One way the user can specify these parameters is indicated by ## the following 'sketch' of the GUI: ## ## FRAMEnames ## VVVVVVVVVV ## ------------------------------------------------------------------------------------------ ## Read an Outline File (e.g. Longitudes-Latitudes) - and Draw the Outline (Map) ## [window title] ## ------------------------------------------------------------------------------------------ ## ## .fRbuttons {Exit} {Help} {(re)Draw} {Reset {Clear} {Background {Line {Text {Write Draw O out O filled ## Parms} Color} Color} Color} PS file} type: line polygon ## ## .fRfile Enter outline-data filename: ______________________________________________ {Browse...} ## ## .fRmsg [ .......... Messages go here, in a label widget .................................... ] ## ## .fRparms Precision Filter: 0.01___ (to filter out any point close to a previous point) Max Image Size (pixels): 600__ ## [a label here contains the text above] ## ## .fRlimits Rectangular Plot Limits: Min-X: -45.0___ Max-X: 45.0___ Min-Y: -45.0___ Max-Y: 45.0___ ## ## .fRcanvas |--------------------------------------------------------------------------------------A ## | | ## | [This scrollable canvas contains the rectangular 'map' image.] | ## | | ## | | ## | | ## | | ## | | ## |<------------------------------------------------------------------------------------>V ## ## ------------------------------------------------------------------- ## ## In the above sketch of the GUI: ## ## SQUARE BRACKETS indicate a comment (not to be placed on the GUI). ## BRACES indicate a Tk 'button' widget. ## UNDERSCORES indicate a Tk 'entry' widget. ## A COLON indicates that the text before the colon is on a 'label' widget. ## CAPITAL-O indicates a Tk 'radiobutton' widget. ## CAPITAL-X indicates a Tk 'checkbutton' widget (if any). ## ## VERTICAL-BARS and horizontal HYPHENS are used to outline a 'canvas' or ## 'listbox' or 'text' widget. ## ## If there are scrollbars: ## Less-than and greater-than signs indicate the left and right ends of a horizontal 'scrollbar'. ## Capital-V and Capital-A letters indicate the bottom and top ends of a vertical 'scrollbar'. ## ##+############## ## GUI components: ## ## From the GUI 'sketch' above, it is seen that the GUI consists of about ## ## - 10 button widgets ## - 10 label widgets ## - 7 entry widgets ## - 1 canvas widget with scrollbars ## - 2 radiobutton widgets in one group ## - 0 scale widgets (but may use scale widgets in place of some entry widgets) ## - 0 checkbutton widgets ## - 0 listbox widgets ## - 0 text widgets ## ##+######################################################################## ## 'CANONICAL' STRUCTURE OF THIS TK CODE: ## ## 0) Set general window & widget parms (win-name, win-position, ## win-color-scheme, fonts, widget-geometry-parms, win-size-control). ## ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack ALL frames and sub-frames. ## ## 2) Define all widgets in the frames. Pack them. ## ## 3) Define keyboard or mouse/touchpad/touch-sensitive-screen action ## BINDINGS, if needed. ## ## 4) Define PROCS, if needed. ## ## 5) Additional GUI INITIALIZATION (typically with one or two of ## the procs), if needed. ## ## ## Some detail about the code structure of this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : '.fRbuttons' ## '.fRfile' ## '.fRmsg' ## '.fRparms' ## '.fRlimits' ## '.fRcanvas' ## No sub-frames. ## ## 1b) Pack ALL frames. ## ## 2) Define all WIDGETS in the frames (and pack them): ## ## - In '.fRbuttons': ## about 8 button widgets ('Exit','Help','(re)Draw', 'ResetParms', ## 'Clear', 'BkgdColor','LineColor','TextColor') ## ## - In '.fRmsg': ## 1 label widget to display messages to the user ## ## - In '.fRfile': ## 1 label and 1 entry and 1 button widget ## ## - In '.fRparms': ## 2 pairs of 'label' and 'entry' widgets ## ## - In '.fRlimits': ## 4 pairs of 'label' and 'entry' widgets ## ## - In '.fRcanvas': 1 'canvas' widget with scrollbars ## ## 3) Define BINDINGS: see the BINDINGS section for bindings, if any ## ## 4) Define PROCS, such as: ## ## - 'get_filename' - called by the 'Browse...' button ## ## - 'read_file' - called by a binding on the filename entry field ## ## - 'reDraw' - called by the '(re)Draw' button ## ## - 'reset_parms' - called by the 'ResetParms' button ## ## - 'setMappingVars_for_px2wc' - called by proc 'reDraw' ## ## - 'Xpx2wc' - called by proc 'reDraw' ## - 'Ypx2wc' - called by proc 'reDraw' ## ## - 'set_background_color0' - called by the 'BackgroundColor' button ## ## - 'set_line_color1' - called by the 'LineColor' button ## ## - 'set_text_color1' - called by the 'TextColor' button ## ## - 'update_color_buttons' - sets background & foreground color of ## either of the 3 color buttons ## ## - 'advise_user' - called by the 'get_filename' and 'proc' procs ## and in the 'Additional GUI Initialization' ## section at the bottom of this script ## ## - 'clear_canvas' - called by the 'Clear' button ## ## - 'edit_inputs' - called by the 'read_file' proc ## ## - 'decimal_check' - called by the 'edit_inputs' proc ## ## - 'popup_msgVarWithScroll' - called by the 'Help' button ## ## See the PROCS ## ## 5) Additional GUI initialization: ## Set some inital values of parameters ## such as the 3 colors and use a call to ## proc 'reset_parms' to initialize entry field values. ## ##+######################################################################## ## DEVELOPED WITH: ## Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october release, 'Karmic Koala'). ## ## $ wish ## % puts "$tcl_version $tk_version" ## showed 8.5 8.5 on Ubuntu 9.10 ## after Tcl-Tk 8.4 was replaced by 8.5 --- to get anti-aliased fonts. ##+####################################################################### ## MAINTENANCE HISTORY: ## Created by: Blaise Montandon 2016nov05 ## Changed by: Blaise Montandon 2016dec03 1) Changed iconname. ## 2) Added 2 new msgs to aRtext array. ## Changed by: Blaise Montandon 2016dec06 1) Change the limits shown in the ## Xmin,Xmax,Ymin,Ymax entry fields. ## 2) Add a margin entry widget to give ## some margin setting control to ## the user. ## Changed by: Blaise Montandon 2016dec08 Added a clear-canvas command to ## the 'read_file' proc. ## Changed by: Blaise Montandon 2016dec13 1) Added an 'Add360' checkbutton and ## updated the 'read_file' proc and ## the HELPtext variable accordingly. ## 2) Moved the outline/filled-polygon ## radiobuttons near the ReDraw button. ## 3) Disable the min-max entry fields ## and set their bkgd color. Also ## make them a little wider. ## 4) Change the format of the messages ## issued by the bind statements. ##+####################################################################### ##+####################################################################### ## Set general window parms (win-title,win-position). ##+####################################################################### wm title . "Read an Outline File (e.g. Longitude-Latitude points) - and Draw the Outline (Map)" wm iconname . "DrawOutline" wm geometry . +15+30 ##+###################################################### ## Set the color scheme for the window and set the ## background color for the 'trough' in some widgets. ##+###################################################### tk_setPalette "#e0e0e0" set entryBKGD "#fcfcfc" set radbuttBKGD "#f0f0f0" set chkbuttBKGD "#f0f0f0" # set scaleBKGD "#f0f0f0" # set listboxBKGD "#f0f0f0" ##+######################################################## ## Use a VARIABLE-WIDTH FONT for label and button widgets. ## ## Use a FIXED-WIDTH FONT for listboxes (and ## entry fields, if any). ##+######################################################## font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_varwidth \ -family {comic sans ms} \ -size -12 \ -weight bold \ -slant roman ## Some other possible (similar) variable width fonts: ## Arial ## Bitstream Vera Sans ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana font create fontTEMP_fixedwidth \ -family {liberation mono} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_fixedwidth \ -family {liberation mono} \ -size -12 \ -weight bold \ -slant roman ## Some other possible fixed width fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+########################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. width and height of canvas, and padding for Buttons) ##+########################################################### ## BUTTON geom parameters: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 set RELIEF_button "raised" ## LABEL geom parameters: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 # set RELIEF_label "ridge" # set RELIEF_label "raised" set RELIEF_label "flat" ## ENTRY widget geom settings: set BDwidthPx_entry 2 ## RADIOBUTTON widget geom settings: set BDwidthPx_radbutt 2 # set RELIEF_radbutt "ridge" set RELIEF_radbutt "raised" ## CHECKBUTTON widget geom settings: set BDwidthPx_chkbutt 2 # set RELIEF_chkbutt "ridge" set RELIEF_chkbutt "raised" # set PADXpx_chkbutt 0 # set PADYpx_chkbutt 0 set PADXpx_chkbutt 2 set PADYpx_chkbutt 2 ## COMMENT some geom settings, for now. if {0} { ## SCALE geom parameters: set BDwidthPx_scale 2 # set initScaleLengthPx 100 set scaleThicknessPx 10 set scaleRepeatDelayMillisecs 800 } ## END OF COMMENTED geom settings ## CANVAS geom parameters: set initCanWidthPx 300 set initCanHeightPx 300 set minCanHeightPx 24 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ##+#################################################################### ## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI. ## NOTE: This can aid INTERNATIONALIZATION. This array can ## be set according to a nation/region parameter. ##+#################################################################### ## if { "$VARlocale" == "en"} ## For widgets in 'fRbuttons' frame: set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(labelRADBUTTS) "PLOT TYPE:" set aRtext(radbuttOUTLINE) "out line" set aRtext(radbuttFILL) "filled polygon" set aRtext(buttonREPLOT) "(re)Draw" set aRtext(buttonSETLIMITS) "ResetParms" set aRtext(buttonCLEAR) "Clear" set aRtext(buttonCOLOR0) "Background Color" set aRtext(buttonCOLOR1) "Line Color" set aRtext(buttonCOLOR2) "Text Color" set aRtext(buttonWRITEPS) "Write PS file" set aRtext(chkbuttADD360) "Add 360 to neg. lon." ## For widgets in 'fRfile' frame: set aRtext(labelFILENAME) \ "Enter outline-data filename:" set aRtext(buttonBROWSE) \ "Browse ..." ## For widgets in 'fRparms' frame: set aRtext(labelFILTERA) "Point Filter Precision:" set aRtext(labelFILTERB) \ "(to filter out any point close to a previous point ; larger filters more)" set aRtext(labelIMGSIZE) " Max Image Size (pixels):" ## For widgets in .fRlimits' frame: set aRtext(labelXMINLIM) "Data Limits - Xmin:" set aRtext(labelXMAXLIM) " Xmax:" set aRtext(labelYMINLIM) " Ymin:" set aRtext(labelYMAXLIM) " Ymax:" set aRtext(labelMARGIN) " Margin (%):" ## For some calls to the 'advise_user' proc: set aRtext(SELECT-LOAD-DRAWmsg) \ "*** After selecting a data file and loading the data, *** *** click '$aRtext(buttonREPLOT)' when ready to do the drawing. ***" set aRtext(HowToLOADmsg) \ "*** After selecting a file, use the Return/Enter key or click *** *** MouseButton3 in the filename entry field to (re)load the data. ***" set aRtext(PARMCHGmsg) \ "*** (re)Load the data and click '$aRtext(buttonREPLOT)' when ready to do the drawing. ***" set aRtext(COLORCHGmsg) \ "*** Click '$aRtext(buttonREPLOT)' when ready to do the drawing. ***" ## END OF if { "$VARlocale" == "en"} ##+################################################################### ## Set a MINSIZE of the window. ## ## For width, allow for the minwidth of the '.fRbuttons' frame: ## about 7 buttons (Exit,Help,ReDraw,ResetParms,Clear, ## BkgdColor,LineColor,TextColor). ## We want to at least be able to see the 'Exit' button. ## ## For height, allow ## 2 chars high for the '.fRbuttons' frame, ## 1 char high for the '.fRfile' frame, ## 2 chars high for the '.fRmsg' frame, ## 1 char high for the '.fRlimits' frame, ## 1 char high for the '.fRparms' frame, ## 24 pixels high for the '.fRcanvas' frame. ##+################################################################### ## MIN WIDTH: set minWinWidthPx [font measure fontTEMP_varwidth \ "$aRtext(buttonEXIT) $aRtext(buttonHELP) $aRtext(buttonREPLOT) \ $aRtext(buttonSETLIMITS) $aRtext(buttonCLEAR) \ Color Color Color"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 8 x 4 pixels/widget for borders/padding for ## 8 widgets. set minWinWidthPx [expr {40 + $minWinWidthPx}] ## MIN HEIGHT --- allow ## 2 chars high for '.fRbuttons' ## 1 char high for '.fRfile' ## 2 chars high for '.fRmsg' ## 1 char high for '.fRlimits' ## 1 char high for '.fRparms' ## 24 pixels high for '.fRcanvas' set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {(7 * $CharHeightPx) + 24}] ## Add about 28 pixels for top-bottom window decoration, ## about 6x4 pixels for each of the 6 stacked frames and their ## widgets (their borders/padding). set minWinHeightPx [expr {$minWinHeightPx + 52}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## We allow the window to be resizable and we pack the canvas with ## '-fill both -expand 1' so that the canvas can be enlarged by enlarging ## the window. ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRbuttons' '.fRfile' '.fRmsg' ## '.fRlimits' '.fRparms' '.fRcanvas' ##+################################################################ ## FOR TESTING change 0 to 1: ## (Example1: To see appearance of frames when borders are drawn.) ## (Example2: To see sizes of frames for various '-fill' options.) ## (Example3: To see how frames expand as window is resized.) if {0} { set RELIEF_frame raised set BDwidthPx_frame 2 } else { set RELIEF_frame flat set BDwidthPx_frame 0 } frame .fRbuttons -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRfile -relief $RELIEF_frame -borderwidth $BDwidthPx_frame # frame .fRfile -relief raised -borderwidth 2 frame .fRmsg -relief raised -borderwidth 2 frame .fRparms -relief $RELIEF_frame -borderwidth $BDwidthPx_frame # frame .fRlimits -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRlimits -relief raised -borderwidth 2 # frame .fRcanvas -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRcanvas -relief raised -borderwidth 2 ##+############################## ## PACK the top-level FRAMES. ##+############################## pack .fRbuttons \ .fRfile \ .fRmsg \ .fRparms \ .fRlimits \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcanvas \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+######################################################### ## OK. Now we are ready to define the widgets in the frames. ##+######################################################### ##+##################################################################### ## In the '.fRbuttons' FRAME --- ## DEFINE about 8 BUTTON widgets. ## Then PACK all these widgets. ##+##################################################################### button .fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRbuttons.buttHELP \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {popup_msgVarWithScroll .topHelp "$HELPtext" +10+10} ## Define the 'draw-style' radiobuttons: label .fRbuttons.labelRADBUTTS \ -text "$aRtext(labelRADBUTTS)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief ridge \ -bd $BDwidthPx_button ## The 'VARdrawtype' variable is used for these radiobuttons. ## The default will be set in the 'Additional GUI Initialization' ## section at the bottom of this script. ## set VARdrawtype "outline" ## set VARdrawtype "fill" radiobutton .fRbuttons.radbuttOUTLINE \ -text "$aRtext(radbuttOUTLINE)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARdrawtype \ -value "outline" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt \ -bd $BDwidthPx_radbutt radiobutton .fRbuttons.radbuttFILL \ -text "$aRtext(radbuttFILL)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARdrawtype \ -value "fill" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt \ -bd $BDwidthPx_radbutt button .fRbuttons.buttREPLOT \ -text "$aRtext(buttonREPLOT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief $RELIEF_radbutt \ -bd $BDwidthPx_button \ -command {reDraw} button .fRbuttons.buttSETLIMITS \ -text "$aRtext(buttonSETLIMITS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {reset_parms} button .fRbuttons.buttCLEAR \ -text "$aRtext(buttonCLEAR)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {clear_canvas} button .fRbuttons.buttCOLOR0 \ -text "$aRtext(buttonCOLOR0)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_background_color0} button .fRbuttons.buttCOLOR1 \ -text "$aRtext(buttonCOLOR1)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_line_color1} button .fRbuttons.buttCOLOR2 \ -text "$aRtext(buttonCOLOR2)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_text_color2} button .fRbuttons.buttWRITEPS \ -text "$aRtext(buttonWRITEPS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {writePSfile} ## The checkbutton variable, FILTERonLIMITS0or1, will be initialized ## in the 'Additional GUI Initialization' section at the bottom of ## this Tk script. Example: ## set FILTERonLIMITS0or1 0 checkbutton .fRbuttons.chkbuttADD360 \ -text "$aRtext(chkbuttADD360)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_chkbutt \ -pady $PADYpx_chkbutt \ -bd $BDwidthPx_chkbutt \ -variable Add360_0or1 \ -selectcolor "$chkbuttBKGD" \ -relief $RELIEF_chkbutt ##+########################################### ## Pack the widgets in the 'fRbuttons' frame. ##+########################################### pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRbuttons.labelRADBUTTS \ .fRbuttons.radbuttOUTLINE \ .fRbuttons.radbuttFILL \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRbuttons.buttREPLOT \ .fRbuttons.buttSETLIMITS \ .fRbuttons.buttCLEAR \ .fRbuttons.buttCOLOR0 \ .fRbuttons.buttCOLOR1 \ .fRbuttons.buttCOLOR2 \ .fRbuttons.buttWRITEPS \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRbuttons.chkbuttADD360 \ -side left \ -anchor w \ -fill none \ -expand 0 ##+################################################################## ## In the '.fRfile' FRAME ---- ## DEFINE 1 LABEL widget, 1 ENTRY widget, and 1 BUTTON widget. ## Then PACK all these widgets. ##+################################################################## label .fRfile.labelFILENAME \ -text "$aRtext(labelFILENAME)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## Someday we may initialize this entry widget variable ## (like some others) in the 'Additional GUI Initialization' ## section at the bottom of this script --- say via the ## 'reset_parms' proc. set ENTRYfilename "" entry .fRfile.entryFILENAME \ -textvariable ENTRYfilename \ -bg $entryBKGD \ -disabledbackground $entryBKGD \ -font fontTEMP_fixedwidth \ -width 30 \ -relief sunken \ -bd $BDwidthPx_entry button .fRfile.buttonBROWSE \ -text "$aRtext(buttonBROWSE)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {get_filename} ##################################### ## Pack the widgets in frame '.fRfile'. ##################################### pack .fRfile.labelFILENAME \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRfile.entryFILENAME \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRfile.buttonBROWSE \ -side left \ -anchor w \ -fill none \ -expand 0 ##+################################################################## ## In the '.fRmsg' FRAME ---- ## DEFINE-and-PACK 1 LABEL widget. ##+################################################################## label .fRmsg.labelINFO \ -text "" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -bg "#ff6666" \ -bd $BDwidthPx_button pack .fRmsg.labelINFO \ -side left \ -anchor w \ -fill x \ -expand 1 ##+################################################################## ## In the '.fRparms' FRAME ---- ## DEFINE 1 pair of LABEL and ENTRY widgets. ## Then PACK all these widgets. ##+################################################################## label .fRparms.labelFILTERA \ -text "$aRtext(labelFILTERA)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## We initialize this widget var (and others) ## in the GUI initialization section at the ## bottom of this script. ## # set ENTRYfiltertol "0.01" entry .fRparms.entryFILTERTOL \ -textvariable ENTRYfiltertol \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width 7 \ -relief sunken \ -bd $BDwidthPx_entry label .fRparms.labelFILTERB \ -text "$aRtext(labelFILTERB)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## Entry widget for image sizing: label .fRparms.labelIMGSIZE \ -text "$aRtext(labelIMGSIZE)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## We initialize this widget var (and others) ## in the GUI initialization section at the ## bottom of this script. ## # set ENTRYimgMaxSizePx "600" entry .fRparms.entryIMGSIZE \ -textvariable ENTRYimgMaxSizePx \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width 6 \ -relief sunken \ -bd $BDwidthPx_entry ##+##################################### ## PACK the widgets in frame '.fRparms'. ##+##################################### pack .fRparms.labelFILTERA \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRparms.entryFILTERTOL \ -side left \ -anchor w \ -fill x \ -expand 0 pack .fRparms.labelFILTERB \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRparms.entryIMGSIZE \ -side right \ -anchor e \ -fill x \ -expand 0 pack .fRparms.labelIMGSIZE \ -side right \ -anchor e \ -fill none \ -expand 0 ##+################################################################## ## In the '.fRlimits' FRAME ---- ## DEFINE 4 pairs of LABEL-and-ENTRY widgets. ## Then PACK all these widgets. ##+################################################################## set WIDTHminmaxENTRY 8 ## For XMIN: label .fRlimits.labelXMINLIM \ -text "$aRtext(labelXMINLIM)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## We initialize this widget var (and others) ## in the GUI initialization section at the ## bottom of this script. ## # set ENTRYxmin "-45.0" entry .fRlimits.entryXMINLIM \ -textvariable ENTRYxmin \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width $WIDTHminmaxENTRY \ -relief sunken \ -bd $BDwidthPx_entry ## For XMAX: label .fRlimits.labelXMAXLIM \ -text "$aRtext(labelXMAXLIM)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## We initialize this widget var (and others) ## in the GUI initialization section at the ## bottom of this script. ## # set ENTRYxmax "45.0" entry .fRlimits.entryXMAXLIM \ -textvariable ENTRYxmax \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width $WIDTHminmaxENTRY \ -relief sunken \ -bd $BDwidthPx_entry ## For YMIN: label .fRlimits.labelYMINLIM \ -text "$aRtext(labelYMINLIM)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## We initialize this widget var (and others) ## in the GUI initialization section at the ## bottom of this script. ## # set ENTRYymin "-45.0" entry .fRlimits.entryYMINLIM \ -textvariable ENTRYymin \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width $WIDTHminmaxENTRY \ -relief sunken \ -bd $BDwidthPx_entry ## For YMAX: label .fRlimits.labelYMAXLIM \ -text "$aRtext(labelYMAXLIM)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## We initialize this widget var (and others) ## in the GUI initialization section at the ## bottom of this script. ## # set ENTRYymax "45.0" entry .fRlimits.entryYMAXLIM \ -textvariable ENTRYymax \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width $WIDTHminmaxENTRY \ -relief sunken \ -bd $BDwidthPx_entry ## Entry widget for plot margin: label .fRlimits.labelMARGIN \ -text "$aRtext(labelMARGIN)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief $RELIEF_label \ -bd $BDwidthPx_label ## We initialize this widget var (and others) ## in the GUI initialization section at the ## bottom of this script. ## # set ENTRYmarginPcent "10.0" entry .fRlimits.entryMARGIN \ -textvariable ENTRYmarginPcent \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width 6 \ -relief sunken \ -bd $BDwidthPx_entry ##+##################################### ## PACK the widgets in frame '.fRlimits'. ##+##################################### pack .fRlimits.labelXMINLIM \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlimits.entryXMINLIM \ -side left \ -anchor w \ -fill x \ -expand 0 pack .fRlimits.labelXMAXLIM \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlimits.entryXMAXLIM \ -side left \ -anchor w \ -fill x \ -expand 0 pack .fRlimits.labelYMINLIM \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlimits.entryYMINLIM \ -side left \ -anchor w \ -fill x \ -expand 0 pack .fRlimits.labelYMAXLIM \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlimits.entryYMAXLIM \ -side left \ -anchor w \ -fill x \ -expand 0 pack .fRlimits.entryMARGIN \ -side right \ -anchor e \ -fill x \ -expand 0 pack .fRlimits.labelMARGIN \ -side right \ -anchor e \ -fill none \ -expand 0 ## For now, disable the 4 min-max entry fields. ## For now, we just use these entry fields to display the data limits ## --- we do not use any changes that might be made to these entry fields. .fRlimits.entryXMINLIM configure -state disabled .fRlimits.entryXMAXLIM configure -state disabled .fRlimits.entryYMINLIM configure -state disabled .fRlimits.entryYMAXLIM configure -state disabled # The disabled entry field background is too dark. Use a bright bkgd. .fRlimits.entryXMINLIM configure -disabledbackground $entryBKGD .fRlimits.entryXMAXLIM configure -disabledbackground $entryBKGD .fRlimits.entryYMINLIM configure -disabledbackground $entryBKGD .fRlimits.entryYMAXLIM configure -disabledbackground $entryBKGD ##+###################################################### ## In the '.fRcanvas' FRAME --- ## DEFINE the 'canvas' widget --- with SCROLLBARS. ## Then PACK the widget(s). ##+###################################################### ## We set highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+###################################################### canvas .fRcanvas.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 \ -yscrollcommand {.fRcanvas.scrolly set} \ -xscrollcommand {.fRcanvas.scrollx set} # -height $initCanHeightPx \ scrollbar .fRcanvas.scrolly \ -orient vertical \ -command {.fRcanvas.can yview} scrollbar .fRcanvas.scrollx \ -orient horizontal \ -command {.fRcanvas.can xview} ##+####################################################### ## PACK the widgets in frame '.fRcanvas'. ##+####################################################### ##+######################################################## ## NOTE: ## NEED TO PACK THE SCROLLBARS BEFORE THE CANVAS WIDGET. ## OTHERWISE THE CANVAS WIDGET TAKES ALL THE FRAME SPACE. ##+####################################################### pack .fRcanvas.scrolly \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRcanvas.scrollx \ -side bottom \ -anchor s \ -fill x \ -expand 0 ##+############################################################## ## !!!NEED TO USE '-expand 0' FOR THE X AND Y SCROLLBARS, so that ## the canvas is allowed to fill the remaining frame-space nicely ## --- without a gap between the canvas and its scrollbars. ##+############################################################## ## NOTE: fill=none and expand=1 does not work well. ## If initial canvas size is 300x24, for example, ## we cannot resize the canvas width to greater than 300 say ## and we cannot resize the canvas height to greater than ## 24. ##+############################################################# pack .fRcanvas.can \ -side top \ -anchor n \ -fill none \ -expand 0 # -fill both \ # -expand 1 ## FOR TESTING: if {0} { puts "" puts "PACK canvas with :" puts " fill : none" puts " expand : 0" } ##+######################################## ## END OF the DEFINITION OF THE GUI WIDGETS ##+######################################## ##+############################### ## BINDINGS SECTION: ##+############################### ##+################################################################ ## Advise user on how to load the data from a selected file ## when the user clicks on the filename 'Browse' button. ##+################################################################ bind .fRfile.buttonBROWSE \ {advise_user "$aRtext(HowToLOADmsg)."} ##+################################################################ ## Read the data file into array variables if the user ## uses the Return key or a mouse-button2 click in the ## filename entry field. ##+################################################################ bind .fRfile.entryFILENAME {read_file} bind .fRfile.entryFILENAME {read_file} ##+################################################################### ## Remind user to RELOAD the data and click 'reDraw' after changing ## the 'filter-tolerance' parameter --- or the 'ADD360' checkbutton. ##+################################################################### bind .fRparms.entryFILTERTOL \ {advise_user "NEW FILTER-PRECISION VALUE may have been set. $aRtext(PARMCHGmsg) "} bind .fRbuttons.chkbuttADD360 \ {advise_user "The 'Add 360' CHECKBUTTON may have been changed. $aRtext(PARMCHGmsg) "} ##+################################################################### ## If we ever use the min-max entry fields for filtering data, then we ## would remind the user to RELOAD the data and click 'reDraw' after ## changing a min or max limit parameter. ##+################################################################### if {0} { bind .fRlimits.entryXMINLIM \ {advise_user "NEW X-MIN drawing-area LIMIT VALUE may have been set. $aRtext(PARMCHGmsg) "} bind .fRlimits.entryXMAXLIM \ {advise_user "NEW X-MAX drawing-area LIMIT VALUE may have been set. $aRtext(PARMCHGmsg) "} bind .fRlimits.entryYMINLIM \ {advise_user "NEW Y-MIN drawing-area LIMIT VALUE may have been set. $aRtext(PARMCHGmsg) "} bind .fRlimits.entryYMAXLIM \ {advise_user "NEW Y-MAX drawing-area LIMIT VALUE may have been set. $aRtext(PARMCHGmsg) "} } ##+###################################################################### ## PROCS SECTION: ## ## - 'get_filename' - called by the 'Browse...' button ## ## - 'read_file' - called by a binding on the filename entry field ## ## - 'reDraw' - called by the '(re)Draw' button ## ## - 'reDraw_outline' - called by the 'reDraw' proc ## ## - 'reDraw_polygon' - called by the 'reDraw' proc ## ## - 'reset_parms' - called by the 'ResetParms' button ## ## - 'setMappingVars_for_px2wc' - called by proc 'reDraw'. ## ## - 'Xpx2wc' - called by proc 'reDraw'. ## - 'Ypx2wc' - called by proc 'reDraw'. ## ## - 'set_background_color0' - called by the 'BackgroundColor' button. ## ## - 'set_line_color1' - called by the 'LineColor' button ## ## - 'set_text_color1' - called by the 'TextColor' button ## ## - 'update_color_buttons' - sets background & foreground color of ## either of the 3 color buttons. ## ## - 'advise_user' - called by 'get_filename' and 'proc' procs ## and in the 'Additional GUI Initialization' ## section at the bottom of this script ## ## - 'clear_canvas' - called by the 'Clear' button ## ## - 'edit_inputs' - called by the 'read_file' proc ## ## - 'decimal_check' - called by 'edit_inputs' proc ## ## - 'numeric_check_string_double' - called by proc 'read_file' ## ## - 'popup_msgVarWithScroll' - called by the 'Help' button. ## ##+####################################################################### ######################################################################## ## PROC: 'get_filename' ######################################################################## ## PURPOSE: To use 'tk_getOpenFile' dialog to get a filename to put ## in the ENTRYfilename variable. ## ## CALLED BY: the 'Browse...' button ######################################################################## proc get_filename {} { global ENTRYfilename CURdir ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax set fName "" set fName [tk_getOpenFile -parent . \ -title "Select a filename of a boundary data file." \ -initialdir "$CURdir" ] ## FOR TESTING: # puts "fName : $fName" if {"$fName" == ""} {return} if {[file exists "$fName"]} { ################################################ ## If the filename from the file selector exits, ## put the name in the filename entry widget, ## removing any leading or trailing spaces. ############################################### set ENTRYfilename [string trim "$fName"] ################################################ ## Make the end of the filename visible in the ## filename entry fields. ################################################ .fRfile.entryFILENAME xview end ################################################# ## Save the current data directory, to quickly ## go to this directory if another file is ## to be selected for data loading. ################################################# set CURdir [ file dirname "$ENTRYfilename" ] ################################################# ## Clear the canvas of a drawing from a previously ## loaded file --- in case that data does not ## correspond to this file. ################################################# .fRcanvas.can delete all ################################################# ## Clear the Xmin,Xmax,Ymin,Ymax entry fields of ## drawing-area limits from a previously loaded file ## --- in case that data does not correspond to ## this file. ################################################# set ENTRYxmin "" set ENTRYxmax "" set ENTRYymin "" set ENTRYymax "" } ###################################################### ## Put the focus on the filename entry field, to ## facilitate causing the data to be loaded from ## the file by simply pressing the Return/Enter key, ## without having to manually direct the focus there. ###################################################### focus .fRfile.entryFILENAME } ## END OF PROC 'get_filename' ######################################################################## ## PROC: 'read_file' ######################################################################## ## PURPOSE: Read the file specified in ENTRYfilename and put ## the x,y coords (of columns 1 and 2 in each file record/line) ## in arrays 'aRdeglat' and 'aRdeglon'. ## ## The index into the arrays is an integer representing the ## count of the data points to be plotted. Note that it may ## be the case that not all the data lines of the file are ## to be plotted. When the 'Filter Precision' parameter is set ## sufficiently small, some data lines may be filtered out. ## ## Note that the x,y data in arrays 'aRdeglat' and 'aRdeglon' ## do not necessarily need to be degrees-decimal data. ## We use these array names because the main target application ## of this 2D plot utility is for making 2D Earth maps. ## ## Also attempt to set a title for the plot in variable ## VARtitle --- using the text in a first comment statement ## statement of the file. ## ('#' in col1 indicates a comment.) ## ## This read proc assumes that whenever a separate 'loop' is ## started in an outline, a null line or comment line indicates ## the start of a new loop. ## ## A third array, 'aRloopnum', is used to store a loop-count ## number for each point stored in the 'aRdeglat' and 'aRdeglon' ## arrays. The index of the 'aRloopnum' array is the same integer ## used for the 'aRdeglat' and 'aRdeglon' arrays. ## ## ## CALLED BY: the a binding on the filename entry field ######################################################################## proc read_file {} { global ENTRYfilename aRdeglat aRdeglon aRloopnum CNTdatalinesToPlot EDITcode \ ENTRYfiltertol VARtitle Add360_0or1 \ ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax ################################################################## ## Open the file corresponding to the filename in the entry field, ## and get a file-handle identifier. ################################################################## set f [open $ENTRYfilename] ## FOR TESTING: if {0} { puts "" puts "PROC 'read_file' :" puts " Opened file $ENTRYfilename" } ########################################################## ## Initialize some variables to be used in the loop below. ########################################################## set TotRecsRead 0 set line "" ########################################################## ## Remove a previous drawing from the canvas. ## (If a different number of points are going to be used, ## because of a new filter-precision, say, we do not want ## the user to be confused by an old drawing on the canvas.) ########################################################## .fRcanvas.can delete all ################################################################ ## The 'filter precision' parm may make the number of datalines ## to be used for plotting less than the number of ## datalines read from the file. We keep track of both. ################################################################ set CNTdatalines 0 set CNTdatalinesToPlot 0 set INaNEWloop0or1 1 set CNTloop 0 ############################################################ ## START of the WHILE-LOOP for the 'gets' file-READING. ## ## The while-test below is equivalent to 'while {![eof $f]}'. ############################################################ while {[eof $f] == 0} { ############################################################ ## GET the next line (up to a line feed) --- and its length. ## Increment TotRecsRead by 1. ############################################################ set lineLen [gets $f line] incr TotRecsRead ################################################# ## If line too long, bail out of this read proc. ################################################# if { $lineLen > 1200 } { popup_msgVarWithScroll .topLineTooLong \ "STOPPED Reading File $ENTRYfilename because a very long line was encountered. ($lineLen characters) Record number: $TotRecsRead First 50 characters of the line: [ string range $line 0 50 ] " +30+30 close $f return } ################################################# ## Remove white space from both ends of the line. ################################################# set line [string trim $line] ########################################### ## Get the first character in the line. ########################################### set FIRSTchar [ string range $line 0 0 ] ################################################################# ## If the record is a comment record and the 1st record read, ## use its contents to set the variable VARtitle. ## We are through with that record, so we 'continue' in order ## to go to the top of this 'while' loop, to read the next line. ################################################################# if {"$FIRSTchar" == "#"} { if {$TotRecsRead == 1} { set VARtitle [string range "$line" 1 end] } } ###################################################### ## If the record is empty or a comment line, skip it ## and 'continue' to read the next rec. ## ## A null record or comment line between data records ## is used to mean that a new loop/polygon is starting. ## We will want to increment 'CNTloop'. ## ## NOTE: ## If there are multiple null or comment records ## encountered, we could be incrementing 'CNTloop' ## when no data lines were found yet, if we increment ## CNTloop at every null or comment line. ## ## We use the variable 'INaNEWloop0or1' to ## indicate the start of a new loop and try not to ## increment 'CNTloop' unless we have hit a data ## line since the last loop. At that time, we set ## INaNEWloop0or1 to zero. ###################################################### if {"$line" == "" || "$FIRSTchar" == "#"} { set INaNEWloop0or1 1 continue } ######################################################## ## At this point $line should not be empty or a comment ## line --- that is, $line should be a data line. ## Count it. ######################################################## incr CNTdatalines ######################################################## ## We first do some 'cleanup' on the data line --- ## concerning commas and tabs and multiple white-space ## characters. ######################################################## ## Replace each comma by a single space. ######################################################## set line [string map {\, \x20} $line] ######################################################## ## Replace each tab by a single space. ######################################################## set line [string map {\t \x20} $line] ######################################################## ## Replace multiple spaces by a single space. ## Ref: page 163 of 'Practical Programming in Tcl & Tk', ## 4th edition, by Welch, Hobbs, Jones. ######################################################## regsub -all {\s+} $line " " line ######################################################### ## Get the strings in the first 2 cols of the record. ## NOTE: ## If there is a 3rd column of data (a third number), ## we ignore it. ######################################################### set TEMPx [lindex $line 0] set TEMPy [lindex $line 1] ## FOR TESTING: if {0} { puts "" puts "Proc 'read_file': (read a data line -- non-null and non-comment)" puts " (still need to check if this is numeric data)" puts " line: $line" puts " CNTdatalines: $CNTdatalines" puts " INaNEWloop0or1: $INaNEWloop0or1" puts " CNTloop: $CNTloop (loop-count not incremented yet)" puts " TEMPx: $TEMPx" puts " TEMPy: $TEMPy" } ######################################################### ## If we get to this point, we want to store TEMPx,TEMPy ## in the x,y value arrays --- and store the current ## loop number in the loop array. ## ## We check the 2 values to see if they are floating-point ## numbers. If not, we pop a msg and exit this read proc. ########################################################## numeric_check_string_double $TEMPx if {$EDITcode != 0} { close $f return } numeric_check_string_double $TEMPy if {$EDITcode != 0} { close $f return } ######################################################## ## At this point, the 2 values seem to be OK, so we will ## load the 2 values into the 2 lon,lat array variables. ## ## If this is the first data line of a new loop, ## we do NOT do a filter-tolerance check, we increment ## CNTdatalinesToPlot, and use that as the index to load ## the 2 values in the lon,lat arrays. Also we increment ## CNTloop and set INaNEWloop0or1 to zero. ## ## If this is NOT the first point in the 'loop', we will ## do the filter-tolerance check. If the point does NOT pass, ## we 'continue' to read the next line. On the other hand, ## if the point DOES pass the filter-tolerance check, we ## increment CNTdatalinesToPlot and use that as the index ## to load the 2 values into the lon,lat arrays. ######################################################### if {$INaNEWloop0or1 == 1} { set INaNEWloop0or1 0 incr CNTloop incr CNTdatalinesToPlot if {$Add360_0or1 == 1 && $TEMPx < 0.0} { set TEMPx [expr {$TEMPx + 360.0}] } set aRdeglon($CNTdatalinesToPlot) $TEMPx set aRdeglat($CNTdatalinesToPlot) $TEMPy set aRloopnum($CNTdatalinesToPlot) $CNTloop ## FOR TESTING: if {0} { puts "" puts "PROC 'read_file' : (found a first point in a 'loop')" puts " line : $line" puts " Cur. lineLen : $lineLen" puts " TotRecsRead : $TotRecsRead" puts " CNTdatalines : $CNTdatalines" puts " CNTdatalinesToPlot: $CNTdatalinesToPlot" puts " aRdeglon($CNTdatalinesToPlot) : $aRdeglon($CNTdatalinesToPlot)" puts " aRdeglat($CNTdatalinesToPlot) : $aRdeglat($CNTdatalinesToPlot)" puts " aRloopnum($CNTdatalinesToPlot) : $aRloopnum($CNTdatalinesToPlot)" } ## If this was the first data record read, ## initialize the vars to save the min,max values ## that are updated below, according to subsquent ## data records. if {$CNTdatalines == 1} { set Xmin $aRdeglon($CNTdatalinesToPlot) set Xmax $aRdeglon($CNTdatalinesToPlot) set Ymin $aRdeglat($CNTdatalinesToPlot) set Ymax $aRdeglat($CNTdatalinesToPlot) } ## Go get the next line. continue } ## At this point, we are at a data line that is NOT the ## first point of a new loop. We want to check if this ## data point passes the filter test. ## ## At this point, CNTdatalinesToPlot contains the index into ## the previously stored point in the 2 lon,lat arrays. ## ## If the distance between both x and y values of this point ## is closer than ENTRYfiltertol to the previous ## x and y values, skip to the next line. ## First, adjust TEMPx if the 'Add360' checkbutton is on. if {$Add360_0or1 == 1 && $TEMPx < 0.0} { set TEMPx [expr {$TEMPx + 360.0}] } set DELx [expr {abs($TEMPx - $aRdeglon($CNTdatalinesToPlot))}] set DELy [expr {abs($TEMPy - $aRdeglat($CNTdatalinesToPlot))}] if {$DELx < $ENTRYfiltertol && $DELy < $ENTRYfiltertol} {continue} ## At this point, we are at a data line that is NOT the ## first point of a new loop --- and it passed the filter test. ## We want to increment CNTdatalinesToPlot and store the x,y ## values in the lon,lat arrays using that index. We also want ## to store the current CNTloop value in the aRloopnum' array ## using that same integer index. incr CNTdatalinesToPlot set aRdeglon($CNTdatalinesToPlot) $TEMPx set aRdeglat($CNTdatalinesToPlot) $TEMPy set aRloopnum($CNTdatalinesToPlot) $CNTloop ## FOR TESTING: if {0} { puts "" puts "PROC 'read_file' : (found a non-first point in a 'loop')" puts " line : $line" puts " Cur. lineLen : $lineLen" puts " TotRecsRead : $TotRecsRead" puts " CNTdatalines : $CNTdatalines" puts " CNTdatalinesToPlot: $CNTdatalinesToPlot" puts " aRdeglon($CNTdatalinesToPlot) : $aRdeglon($CNTdatalinesToPlot)" puts " aRdeglat($CNTdatalinesToPlot) : $aRdeglat($CNTdatalinesToPlot)" puts " aRloopnum($CNTdatalinesToPlot) : $aRloopnum($CNTdatalineUSED)" } ############################################################### ## Set the min,max of the x,y variables. ## (The first data line of the current 'loop' was used, ## above, to initialize the Xmin, Xmax, Ymin, Ymax variables.) ############################################################### if {$aRdeglon($CNTdatalinesToPlot) < $Xmin} {set Xmin $aRdeglon($CNTdatalinesToPlot)} if {$aRdeglon($CNTdatalinesToPlot) > $Xmax} {set Xmax $aRdeglon($CNTdatalinesToPlot)} if {$aRdeglat($CNTdatalinesToPlot) < $Ymin} {set Ymin $aRdeglat($CNTdatalinesToPlot)} if {$aRdeglat($CNTdatalinesToPlot) > $Ymax} {set Ymax $aRdeglat($CNTdatalinesToPlot)} } ## END OF while {![eof $f]} --- the data file reading #################################### ## Close the file. #################################### close $f ############################################################# ## Put the data limits --- Xmin,Xmax,Ymin,Ymax --- ## in the plot limit entry fields: ## ENTRYxmin, ENTRYxmax, ENTRYymin, ENTRYymax ############################################################# set ENTRYxmin $Xmin set ENTRYxmax $Xmax set ENTRYymin $Ymin set ENTRYymax $Ymax ######################################################### ## If there are a lot of data lines in the file ## (say more than 200), in a message to the user, ## set a reasonable estimate for ENTRYfiltertol ## (a 'suggested' tolerance value) ## as a proportion (say 0.003 - 3 parts in a thousand) ## of the smaller of xmax-minus-xmin and ## ymax-minus-ymin (in world coordinates). ################################################# set TOLmsg "" if {$CNTdatalines > 200} { set TEMPdiffX [expr {$Xmax - $Xmin}] set TEMPdiffY [expr {$Ymax - $Ymin}] set TEMPdiff $TEMPdiffX if {$TEMPdiff > $TEMPdiffY} {set TEMPdiff $TEMPdiffY} set SUGGESTEDtol [expr {0.003 * $TEMPdiff}] if {$SUGGESTEDtol < $ENTRYfiltertol} { set SUGGESTEDtolFMTD [format "%6.5f" $SUGGESTEDtol] set TOLmsg \ "** To use more points for the outline, you may want to try a lower filter-precision value ** --- say $SUGGESTEDtolFMTD instead of $ENTRYfiltertol --- and re-read the file." } } ## END OF if {$CNTdatalines > 200} ############################################### ## Show how many lines were read from the file ## --- in the message line of the GUI. ############################################### advise_user \ "** FINISHED READING FILE. Total Lines Read: $TotRecsRead ** Total 'Filter-Distinct' Data Points/Lines Read: $CNTdatalinesToPlot ** out of the Total Data Lines Read: $CNTdatalines $TOLmsg ** Click the '(re)Draw' button to plot this data." } ## END OF PROC 'read_file' ##+##################################################################### ## PROC: reDraw ## ## PURPOSE: Calls 'reDraw_outline' or 'reDraw_polygon' depending ## on the value of VARdrawtype. ## ## CALLED BY: the 'reDraw' button ##+##################################################################### proc reDraw {} { global VARdrawtype ENTRYfilename CNTdatalinesToPlot EDITcode ################################################################## ## If the filename field is empty, bail out of this proc. ################################################################## if {"$ENTRYfilename" == ""} { advise_user "** There is NO FILENAME specified. **" return } ################################################################## ## If the count of lines read does not exist or if the count ## is less than 1, bail out of this proc. ################################################################## if {[info exists CNTdatalinesToPlot] == 0} { advise_user "** It appears that the file DATA HAS NOT BEEN LOADED. **" return } if {$CNTdatalinesToPlot < 1} { advise_user \ "** It appears that the NO DATA in the file SATISFIED LOAD CRITERIA. **" return } ######################################################## ## Check the entry field inputs. Bail out if there seems ## to be invalid input. ######################################################## edit_inputs if {$EDITcode > 0} {return} ######################################################### ## Run the 'outline' or 'polygon' plot proc. ######################################################### ## FOR TESTING: if {0} { puts "" puts "PROC 'reDraw' :" puts " VARdrawtype: $VARdrawtype" } if {"$VARdrawtype" == "fill"} { reDraw_polygon } else { reDraw_outline } ############################################################# ## We can use a 'wm geometry' command to get the window (and ## the canvas widget) to resize appropriately --- even after ## the user has manually resized the top window. ## ## Reference: wiki.tcl.tk/10720 and wiki.tcl.tk/44 ## and page 237 of Ousterhout's book 'Tcl and the Tk Toolkit': ## "If you would like to restore a window to its natural ## size, you can invoke 'wm geometry' with an empty ## geometry string." ############################################################# wm geometry . {} } ## END OF PROC 'reDraw' ##+##################################################################### ## PROC: reDraw_outline ## ## PURPOSE: ## Given the data in arrays 'aRdeglat' and 'aRdeglon' and ## 'aRloopnum', this proc draws groups of the data ## as line-segments on the canvas using 'create line' statements. ## ## Each line-segment connects a current point to a previous point ## in the 'aRdeglat' and 'aRdeglon' arrays --- until there is ## a 'break' in the value of the loop-number in the array 'aRloopnum'. ## ## This proc skips drawing a line-segment when there is a break ## in the loop numbers. In other words, each 'loop' in the drawing ## is determined when there is a break' in the value of the ## loop-number in the array 'aRloopnum'. ## ## The text in the VARtitle variable is used to put text on ## the canvas. ## ## METHOD: ## The ENTRYfiltertol variable is used to skip plotting ## points that are near the previous point. ## ## The 'setMappingVars_for_px2wc' and 'Xwc2px' and 'Ywc2px' ## procs are used to convert the 'world coordinates' data ## in the 'aRdeglat' and 'aRdeglon' arrays to ## 'pixel coordinates' --- for plotting on the canvas. ## ## CALLED BY: the 'reDraw' proc ##+##################################################################### proc reDraw_outline {} { ## FOR TESTING: (dummy out this proc) # return global ENTRYfilename aRdeglat aRdeglon aRloopnum \ VARtitle CNTdatalinesToPlot \ ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax \ PXperWCx PXperWCy EDITcode \ COLOR0r COLOR0g COLOR0b COLOR0hex \ COLOR1r COLOR1g COLOR1b COLOR1hex \ COLOR2r COLOR2g COLOR2b COLOR2hex ############################################################ ## Clear the message area --- replace with an in-progress msg. ############################################################ advise_user \ "** PLOTTING OF OUTLINES IS IN PROGRESS. ** Please wait." ############################################################ ## Prepare the canvas and point-mapping parameters. ############################################################ prep_canvas_mapping ######################################################### ## In a loop from 1 to CNTdatalinesToPlot, ## start drawing the line-segments using the two arrays ## 'aRdeglon' and 'aRdeglat'. ######################################################### ## If we hit a new loopnumber --- in $aRloopnum($idx) --- ## we skip drawing a line-segment, to start a new ## loop/polygon. ######################################################### set PREVloopnum 0 for {set idx 1} {$idx <= $CNTdatalinesToPlot} {incr idx} { ## If there is a break in the loop numbers in the ## loop number array, reset the var PREVloopnum ## and skip to the next array index to start drawting. if {$aRloopnum($idx) != $PREVloopnum} { set PREVloopnum $aRloopnum($idx) continue } ## At this point, $aRloopnum($idx) is the same as ## PREVloopnum, so we are in the same loop and it ## is time to start drawting line segments. set idx_1 [expr {$idx - 1}] set X1px [Xwc2px $aRdeglon($idx_1)] set Y1px [Ywc2px $aRdeglat($idx_1)] set X2px [Xwc2px $aRdeglon($idx)] set Y2px [Ywc2px $aRdeglat($idx)] .fRcanvas.can create line \ $X1px $Y1px \ $X2px $Y2px \ -tags TAGline -fill "$COLOR1hex" } ## END OF 'for' loop over $idx ######################################################### ## Draw a text title on the canvas --- say ## near the upper-left corner of the canvas. ######################################################### .fRcanvas.can create text 10 10 -anchor nw \ -fill "$COLOR2hex" -tags TAGtext -text "$VARtitle" ############################################################### ## Clear the message area --- replace with a draw completed msg. ############################################################### advise_user \ "** OUTLINE PLOT COMPLETED. ** Number of Points Used: $CNTdatalinesToPlot" ## A sample 'format' statement --- for text output of ## floating-point (decimal) numerics in a message or listing. ## ## set numFMTED [format "%6.2f" $num] } ## END OF PROC 'reDraw_outline' ##+##################################################################### ## PROC: reDraw_polygon ## ## PURPOSE: ## Given the data in arrays 'aRdeglat' and 'aRdeglon' and ## 'aRloopnum', this proc draws groups of the data ## as a 'filled polygon' --- using a 'create polygon' statement ## for each 'loop' of data. ## ## Each loop is determined when there is a break' in the ## value of the loop-number in the array 'aRloopnum'. ## ## The text in the VARtitle variable is used to put text on ## the canvas. ## ## METHOD: ## The ENTRYfiltertol variable is used to skip drawting ## points that are near the previous point. ## ## The 'setMappingVars_for_px2wc' and 'Xwc2px' and 'Ywc2px' ## procs are used to convert the 'world coordinates' data ## in the 'aRdeglat' and 'aRdeglon' arrays to ## 'pixel coordinates' --- for drawting on the canvas. ## ## CALLED BY: the 'reDraw' proc ##+##################################################################### proc reDraw_polygon {} { ## FOR TESTING: (dummy out this proc) # return global ENTRYfilename aRdeglat aRdeglon aRloopnum VARtitle CNTdatalinesToPlot \ ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax \ PXperWCx PXperWCy EDITcode \ COLOR0r COLOR0g COLOR0b COLOR0hex \ COLOR1r COLOR1g COLOR1b COLOR1hex \ COLOR2r COLOR2g COLOR2b COLOR2hex ############################################################ ## Clear the message area --- replace with an in-progress msg. ############################################################ advise_user \ "** PLOTTING POLYGONS IN PROGRESS. ** Please wait." ############################################################ ## Prepare the canvas and point-mapping parameters. ############################################################ prep_canvas_mapping ######################################################### ## In a loop from 1 to CNTdatalinesToPlot, ## start drawing the polygons using the two arrays ## 'aRdeglon' and 'aRdeglat' --- and using the array ## 'aRloopnum' to determine when to start a new polygon. ######################################################### ## If we hit a new loopnumber --- in $aRloopnum($idx) --- ## we start building a new string of point coordinate-pairs ## for use in executing a 'create polygon' command when ## we encounter a change in loopnum as we advance idx. ######################################################### set PREVloopnum 0 set TEMPcoords "" for {set idx 1} {$idx <= $CNTdatalinesToPlot} {incr idx} { ###################################################### ## If there is a break in the loop numbers in the ## loop number array, ## - draw the current contents of TEMPcoords, ## if it is not empty, and ## - reset the var PREVloopnum from the current idx ## - reset TEMPcoords to "" ## - continue building a new TEMPcoords. ###################################################### if {$aRloopnum($idx) != $PREVloopnum} { if {"$TEMPcoords" != ""} { eval .fRcanvas.can create polygon $TEMPcoords \ -tags TAGline -fill "$COLOR1hex" } set PREVloopnum $aRloopnum($idx) set TEMPcoords "" } ## At this point, $aRloopnum($idx) is the same as ## PREVloopnum, so we are in the same loop and it ## is time to start rebuilding TEMPcoords. set Xpx [Xwc2px $aRdeglon($idx)] set Ypx [Ywc2px $aRdeglat($idx)] set TEMPcoords "$TEMPcoords $Xpx $Ypx" ## FOR TESTING: if {0} { set LENcoords [string length $TEMPcoords] puts "" puts "PROC 'reDraw_polygon' : (loaded another point in string TEMPcoords)" puts " idx : $idx" puts " aRdeglon($idx) : $aRdeglon($idx)" puts " aRdeglat($idx) : $aRdeglat($idx)" puts " Xpx : $Xpx" puts " Ypx : $Ypx" puts " Length of TEMPcoords: $LENcoords" } } ## END OF 'for' loop over $idx ######################################################### ## If there is a remaining loop to draw, draw it. ######################################################### if {"$TEMPcoords" != ""} { eval .fRcanvas.can create polygon $TEMPcoords \ -tags TAGline -fill "$COLOR1hex" } ######################################################### ## Draw a text title on the canvas --- say ## near the upper-left corner of the canvas. ######################################################### .fRcanvas.can create text 10 10 -anchor nw \ -fill "$COLOR2hex" -tags TAGtext -text "$VARtitle" ############################################################### ## Clear the message area --- replace with a draw completed msg. ############################################################### advise_user \ "** POLYGON PLOT COMPLETED. ** Number of Points Used: $CNTdatalinesToPlot" ## A sample 'format' statement --- for text output of ## floating-point (decimal) numerics in a message or listing. ## ## set numFMTED [format "%6.2f" $num] } ## END OF PROC 'reDraw_polygon' ##+##################################################################### ## PROC: prep_canvas_mapping ## ## PURPOSE: Prepares canvas and mapping variables to use the ## lon,lat data arrays to plot either an outline or a ## polygon. ## ## NOTE: For this we do NOT need the data arrays ## aRdeglon aRdeglat aRloopnum and their size ## CNTdatalinesToPlot. I.e. we do NOT need the ## point coordinates from the outline file. ## ## We just need the image limits in world-coordinates ## and pixels --- namely, we need the parameters ## ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax and ## ENTRYimgMaxSizePx (and ENTRYmarginPcent) ## from the 'entry' widgets of the GUI. ## ## NOTE: The user is allowed to change the values in fields ## ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax ## from the values Xmin Xmax Ymin Ymax that were ## determined from the data in the first reading ## of the input file. ## (The ways that the variables ## ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax ## are used in the 'read' and 'draw' procs is ## not finalized as of 2016 Dec 06. ## They may eventually be used in the 'read' proc ## to filter out data points that are loaded into ## the lon,lat arrays --- when the entries are not blank, ## i.e. after the 1st read of data from the input file.) ## ## CALLED BY: procs 'reDraw_outline' and 'reDraw_polygon' ##+##################################################################### proc prep_canvas_mapping {} { global ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax ENTRYmarginPcent \ ENTRYimgMaxSizePx PXperWCx PXperWCy \ COLOR0r COLOR0g COLOR0b COLOR0hex \ newImgWidthPx newImgHeightPx ############################################################ ## Clear the canvas, including any lines and text. ############################################################ .fRcanvas.can delete all ############################################################ ## Set the background color of the canvas area from the ## current background color variable. ############################################################ .fRcanvas.can config -bg $COLOR0hex ################################################################ ## Set the variables for converting pixels to world-coords ## using proc 'setMappingVars_for_px2wc'. ################################################################ ## Recall input vars for proc 'setMappingVars_for_px2wc' are: ## xORy,ULwcX,ULwcY,ULpxX,ULpxY,LRwcX,LRwcY,LRpxX,LRpxY ## where UL = upper-left and LR = lower-right. ################################################################ ## We map the upper-left of the image area: ## world-coordinates = Xmin,Ymax (or a little bigger) ## TO pixel-coordinates = 0,0 ## ## AND ## ## We map the lower-right of the image area: ## world-coordinates = Xmax,Ymin (or a little bigger) ## TO pixel-coordinates = curCanWidthPx,curCanHeightPx ## ## To preserve the SAME world-coordinate UNITS in the BOTH ## the x AND y directions, we may want to use the same wc2px ## mapping ratio to convert both x and y world-coordinates to ## pixel coordinates. ## ## The 'xORy' parameter above can be used to control this feature ## of mapping world-coordinates to pixel-coordinates. ## The variable 'xORy' can be 'x' or 'y' or 'min' or 'max' or 'xy'. ## ## See code and comments in 'setMappingVars_for_px2wc' for details. ######################################################################## ############################################################# ## First, set x,y margin values in world-coordinates --- ## to provide a margin --- of say 10 percent (0.1) --- ## around the line-segments of the image. ############################################################# set DELTAx [expr {$ENTRYxmax - $ENTRYxmin}] set DELTAy [expr {$ENTRYymax - $ENTRYymin}] set MARGINxwc [expr {($ENTRYmarginPcent / 100.0) * $DELTAx}] set MARGINywc [expr {($ENTRYmarginPcent / 100.0) * $DELTAy}] ###################################################################### ## Set the world-coordinates of the UPPER-LEFT corner of the draw area. ###################################################################### set XwcUL [expr {$ENTRYxmin - $MARGINxwc}] set YwcUL [expr {$ENTRYymax + $MARGINywc}] ####################################################################### ## Set the world-coordinates of the LOWER-RIGHT corner of the draw area. ####################################################################### set XwcLR [expr {$ENTRYxmax + $MARGINxwc}] set YwcLR [expr {$ENTRYymin - $MARGINywc}] ################################################################# ## Set the aspect ratio of the world-coordinates data. ## (We assume that DELTAx will not be zero.) ################################################################# set ASPECTyTOx [expr {$DELTAy / $DELTAx}] ################################################################# ## Use the world-coordinates aspect ratio to determine an image ## width and height in pixels, based on the requested ## max image size (in pixels), ENTRYimgMaxSizePx. ################################################################# if {$DELTAx > $DELTAy} { set newImgWidthPx $ENTRYimgMaxSizePx set newImgHeightPx [expr {int($ASPECTyTOx * $newImgWidthPx)}] } else { set newImgHeightPx $ENTRYimgMaxSizePx ## We assume that DELTAy will not be zero. set newImgWidthPx [expr {int($newImgHeightPx / $ASPECTyTOx)}] } ## FOR TESTING: if {0} { puts "" puts "PROC 'prep_canvas_mapping' :" puts " newImgWidthPx : $newImgWidthPx" puts " newImgHeightPx : $newImgHeightPx" } ################################################################# ## Call on proc 'setMappingVars_for_px2wc' to set the two ## global (mapping) variables PXperWCx and PXperWCy according to ## 4 entry variables for xmin,xmax,ymin,ymax (in 'world coordinates') ## and according to the requested image size (in pixels). ## ## NOTE: ## We use the 'xy' option which sets both PXperWCx and PXperWCy ## 'independently'. ## ## NOTE: ## We use variables newImgWidthPx and newImgHeightPx which sizes ## the canvas according to the aspect ratio of the data and the ## requested max image size. ################################################################# setMappingVars_for_px2wc xy \ $XwcUL $YwcUL 0 0 \ $XwcLR $YwcLR $newImgWidthPx $newImgHeightPx ############################################################# ## Use 'scrollregion' to make the canvas scrollbars usable ## for a large image. ############################################################# .fRcanvas.can configure -scrollregion "0 0 $newImgWidthPx $newImgHeightPx" ############################################################### ## Configure the canvas width and height to make sure that ## the entire requested drawing-area can be shown on the canvas. ############################################################### .fRcanvas.can configure -width $newImgWidthPx .fRcanvas.can configure -height $newImgHeightPx } ## END OF PROC 'prep_canvas_mapping' ##+##################################################################### ## PROC: reset_parms ## ## PURPOSE: ## Sets values in the xmin,xmax,ymin,ymax fields on the GUI. ## ## CALLED BY: the 'ResetParms' proc. ##+#################################################################### proc reset_parms {} { ## FOR TESTING: (dummy out this proc) # return global ENTRYfiltertol ENTRYimgMaxSizePx \ ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax ENTRYmarginPcent # set ENTRYfiltertol "0.1" # set ENTRYfiltertol "0.01" set ENTRYfiltertol "0.001" set ENTRYimgMaxSizePx "600" set ENTRYxmin "" set ENTRYxmax "" set ENTRYymin "" set ENTRYymax "" set ENTRYmarginPcent "10.0" } ## END OF PROC 'reset_parms' ##+######################################################################## ## PROC: setMappingVars_for_px2wc ##+######################################################################## ## PURPOSE: Sets up 'constants' to be used in converting between x,y ## 'world coordinates' and 'pixel coordinates' on a Tk canvas. ## ## Puts the constants in global variables: ## PXperWC BASEwcX BASEwcY BASEpxX BASEpxY ## ## These variables are for use by 'Xwc2px' and 'Ywc2px' procs ## and 'Xpx2wc' and 'Ypx2wc' procs. ## ## The 'BASE' variables are coordinates of the upper-left point ## of the 'drawting rectangle' --- in world coordinates and ## in pixel coordinates. ## ## METHOD: This proc takes the coordinates of an UpperLeft (UL) ## point and a LowerRight (LR) point --- in both ## 'world coordinates' and 'pixel coordinates' and ## sets some global variables to the used by the ## other drawing procs --- mainly the ratio: ## ## the number-of-pixels-per-world-coordinate-unit, ## in global variable 'PXperWC' ## ## (This will generally include a fractional amount, ## i.e. it is not necessarily an integer.) ## ## INPUTS: ## ## At least eight numbers are input to this proc, as indicated by: ## ## ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY ## ## Generally, the 'wc' inputs may be floating point numbers, and the ## 'px' inputs will generally be (non-negative) integers. ## ## Example: (for a draw area with x between -1.2 and +1.2 ## and with y between -0.2 and +1.2) ## setMappingVars_for_px2wc xy -1.2 1.2 0 0 1.2 -0.2 $canvasWidthPx $canvasHeightPx ## ## The first argument can be either 'x' or 'y' or 'xy'. This determines whether ## global variable 'PXperWC' is detemined by just the X-numbers, just the Y-numbers, ## or both. In this script, we use 'xy' (both). ## ## An 'adjustYpx' global variable can be used to adjust if the pixels ## on a user's monitor are not square. ## ## OUTPUTS: global variables PXperWC BASEwcX BASEwcY BASEpxX BASEpxY ## ## CALLED BY: by the animate' proc. ##+######################################################################## proc setMappingVars_for_px2wc {xORy ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY} { global PXperWCx PXperWCy BASEwcX BASEwcY BASEpxX BASEpxY adjustYpx ## FOR TESTING: (to dummy out this proc) # return ############################################################ ## Calculate PXperWCx and PXperWCy ## (pixels-per-world-coordinate-unit) --- the ratio ## of pixels-per-world-coordinate in the x and y directions, ## for the given UL and LR values. ############################################################ set PXperWCx [expr {abs(($LRpxX - $ULpxX) / ($LRwcX - $ULwcX))}] set PXperWCy [expr {abs(($LRpxY - $ULpxY) / ($LRwcY - $ULwcY))}] ## FOR TESTING: if {0} { puts "proc 'setMappingVars_for_px2wc':" puts "LRwcY: $LRwcY ULwcY: $ULwcY LRwcX: $LRwcX ULwcX: $ULwcX" puts "PXperWCx: $PXperWCx" puts "LRpxY: $LRpxY ULpxY: $ULpxY LRpxX: $LRpxX ULpxX: $ULpxX" puts "PXperWCy: $PXperWCy" } ############################################################# ## Reset PXperWCx and PXperWCy according to whether input ## variable 'xORy' is 'x' or 'y' or 'min' or 'xy'. ## ## For 'x', we set PXperWCy equal to PCperWcx. ## ## For 'y', we set PXperWCx equal to PCperWcy. ## ## For 'min', we set PXperWCx and PXperWCy to the smaller ## of PXperWCx and PXperWCy. ## ## For 'max', we set PXperWCx and PXperWCy to the larger ## of PXperWCx and PXperWCy. ## ## For 'xy', we will leave PXperWCx and PXperWCy unchanged. ############################################################# if {$xORy == "x"} { set PXperWCy $PXperWCx } elseif {$xORy == "y"} { set PXperWCx $PXperWCy } elseif {$xORy == "min"} { if {$PXperWCx > $PXperWCy} { set PXperWCx $PXperWCy } else { set PXperWCy $PXperWCx } } elseif {$xORy == "max"} { if {$PXperWCx > $PXperWCy} { set PXperWCy $PXperWCx } else { set PXperWCx $PXperWCy } } ## END OF if {$xORy == "x"} ############################################################ ## In case the pixels are not square, provide a factor ## that can be used to adjust in the Y direction. ############################################################ set adjustYpx 1.0 ############################################################ ## Set BASEwcX, BASEwcY, BASEpxX and BASEpxY. ############################################################ set BASEwcX $ULwcX set BASEwcY $ULwcY set BASEpxX $ULpxX set BASEpxY $ULpxY ## FOR TESTING: if {0} { puts "proc 'setMappingVars_for_px2wc':" puts "PXperWCx: $PXperWCx PXperWCy: $PXperWCy" puts "BASEwcX: $BASEwcX BASEwcY: $BASEwcY" puts "BASEpxX: $BASEpxX BASEpxY: $BASEpxY" } } ## END OF PROC 'setMappingVars_for_px2wc' ##+######################################################################## ## PROC: Xwc2px ##+######################################################################## ## PURPOSE: Converts an x world-coordinate to pixel units. ## ## CALLED BY: the 'draw' procs ##+######################################################################## proc Xwc2px {x} { global PXperWCx BASEwcX BASEpxX set px [expr {($x - $BASEwcX) * $PXperWCx + $BASEpxX}] return $px } ## END OF PROC 'Xwc2px' ##+######################################################################## ## PROC: Xpx2wc ##+######################################################################## ## PURPOSE: Converts an x-pixel unit to an x-world-coordinate value. ## ## CALLED BY: not used in this app ; here just in case we need it ##+######################################################################## proc Xpx2wc {px} { global PXperWCx BASEwcX BASEpxX set x [expr {( ($px - $BASEpxX) / $PXperWCx ) + $BASEwcX }] return $x } ## END OF PROC 'Xpx2wc' ##+######################################################################## ## PROC: Ywc2px ##+######################################################################## ## PURPOSE: Converts an y world-coordinate to pixel units. ## ## CALLED BY: the 'draw' procs ##+######################################################################## proc Ywc2px {y} { global PXperWCy BASEwcY BASEpxY adjustYpx set px [expr {($BASEwcY - $y) * $PXperWCy * $adjustYpx + $BASEpxY}] return $px } ## END OF PROC 'Ywc2px' ##+######################################################################## ## PROC: Ypx2wc ##+######################################################################## ## PURPOSE: Converts a y-pixel unit to a y-world-coordinate value. ## ## CALLED BY: not used in this app ; here just in case we need it ##+######################################################################## proc Ypx2wc {px} { global PXperWCy BASEwcY BASEpxY adjustYpx set y [expr { $BASEwcY - ( ($px - $BASEpxY) / ( $PXperWCy * $adjustYpx ) ) }] return $y } ## END OF PROC 'Ypx2wc' ##+##################################################################### ## PROC: 'set_background_color0' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set a canvas-background color. ## ## Arguments: global variables ## ## CALLED BY: .fRbuttons.buttCOLOR0 button ##+##################################################################### proc set_background_color0 {} { global COLOR0r COLOR0g COLOR0b COLOR0hex ColorSelectorScript aRtext ## FOR TESTING: # puts "COLOR0r: $COLOR0r" # puts "COLOR0g: $COLOR0g" # puts "COLOR0b: $COLOR0b" set TEMPrgb [ exec $ColorSelectorScript $COLOR0r $COLOR0g $COLOR0b] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLOR0hex "#$hexRGB" set COLOR0r $r255 set COLOR0g $g255 set COLOR0b $b255 ## Set background-and-foreground colors of the indicated color button. update_color_buttons color0 advise_user "NEW BACKGROUND COLOR may have been chosen. $aRtext(COLORCHGmsg) " } ## END OF proc 'set_background_color0' ##+##################################################################### ## PROC: 'set_line_color1' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set a canvas-line-segment color. ## ## Arguments: global variables ## ## CALLED BY: .fRbuttons.buttCOLOR1 button ##+##################################################################### proc set_line_color1 {} { global COLOR1r COLOR1g COLOR1b COLOR1hex ColorSelectorScript aRtext ## FOR TESTING: # puts "COLOR1r: $COLOR1r" # puts "COLOR1g: $COLOR1g" # puts "COLOR1b: $COLOR1b" set TEMPrgb [ exec $ColorSelectorScript $COLOR1r $COLOR1g $COLOR1b] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLOR1hex "#$hexRGB" set COLOR1r $r255 set COLOR1g $g255 set COLOR1b $b255 ## Set background-and-foreground colors of the indicated color button. update_color_buttons color1 advise_user "NEW LINE COLOR may have been chosen. $aRtext(COLORCHGmsg) " } ## END OF proc 'set_line_color1' ##+##################################################################### ## PROC: 'set_text_color2' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set a text-on-canvas color. ## ## Arguments: global variables ## ## CALLED BY: .fRbuttons.buttCOLOR2 button ##+##################################################################### proc set_text_color2 {} { global COLOR2r COLOR2g COLOR2b COLOR2hex ColorSelectorScript aRtext ## FOR TESTING: # puts "COLOR2r: $COLOR2r" # puts "COLOR2g: $COLOR2g" # puts "COLOR2b: $COLOR2b" set TEMPrgb [ exec $ColorSelectorScript $COLOR2r $COLOR2g $COLOR2b] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLOR2hex "#$hexRGB" set COLOR2r $r255 set COLOR2g $g255 set COLOR2b $b255 ## Set background-and-foreground colors of the indicated color button. update_color_buttons color2 advise_user "NEW TEXT COLOR may have been chosen. $aRtext(COLORCHGmsg) " } ## END OF proc 'set_text_color2' ##+##################################################################### ## PROC: 'update_color_buttons' ##+##################################################################### ## PURPOSE: ## This procedure is invoked to set the background color of the ## color button, indicated by the 'colorID' string, ## to its currently set 'colorID' color --- and sets ## foreground color, for text on the button, to a suitable black or ## white color, so that the label text is readable. ## ## Arguments: global color vars ## ## CALLED BY: in 3 'set_*_color*' procs ## and in the additional-GUI-initialization section at ## the bottom of this script. ##+##################################################################### proc update_color_buttons {colorID} { global COLOR0r COLOR0g COLOR0b COLOR0hex global COLOR1r COLOR1g COLOR1b COLOR1hex global COLOR2r COLOR2g COLOR2b COLOR2hex # set colorBREAK 300 set colorBREAK 350 if {"$colorID" == "color1"} { .fRbuttons.buttCOLOR1 configure -bg $COLOR1hex set sumCOLOR1 [expr {$COLOR1r + $COLOR1g + $COLOR1b}] if {$sumCOLOR1 > $colorBREAK} { .fRbuttons.buttCOLOR1 configure -fg "#000000" } else { .fRbuttons.buttCOLOR1 configure -fg "#ffffff" } } elseif {"$colorID" == "color2"} { .fRbuttons.buttCOLOR2 configure -bg $COLOR2hex set sumCOLOR1 [expr {$COLOR2r + $COLOR2g + $COLOR2b}] if {$sumCOLOR1 > $colorBREAK} { .fRbuttons.buttCOLOR2 configure -fg "#000000" } else { .fRbuttons.buttCOLOR2 configure -fg "#ffffff" } } elseif {"$colorID" == "color0"} { .fRbuttons.buttCOLOR0 configure -bg $COLOR0hex set sumCOLOR0 [expr {$COLOR0r + $COLOR0g + $COLOR0b}] if {$sumCOLOR0 > $colorBREAK} { .fRbuttons.buttCOLOR0 configure -fg "#000000" } else { .fRbuttons.buttCOLOR0 configure -fg "#ffffff" } } else { ## Seems to be an invalid colorID. return } } ## END OF PROC 'update_color_buttons' ##+##################################################################### ## PROC: 'advise_user' ##+##################################################################### ## PURPOSE: Puts a message to the user on the GUI. ## ## CALLED BY: in three 'set_*_color*' procs, ## in some 'bind' statements in the BIND section above, ## and in the additional-GUI-initialization section at ## the bottom of this script. ##+##################################################################### proc advise_user {text} { .fRmsg.labelINFO configure -text "$text" ## Make sure the text is displayed on the GUI. update ## Alternatively, we could put the message in the title-bar ## of the GUI window. (But it is easy for the user to ## fail to see the message there. Besides, we have more ## options in displaying the message by putting it on a ## Tk widget in the GUI.) ## # wm title . "$text" } ## END OF PROC 'advise_user' ##+#################################################################### ## PROC: 'clear_canvas' ##+#################################################################### ## PURPOSE: Clears the lines and text from the canvas. ## ## CALLED BY: the 'Clear' button ##+#################################################################### proc clear_canvas {} { .fRcanvas.can delete all } ## END OF PROC 'clear_canvas' ##+#################################################################### ## PROC: 'edit_inputs' ##+##################################################################### ## PURPOSE: Checks entry widgets entries and pops up an error message ## if the data is invalid. ## ## CALLED BY: the 'reDraw' proc ##+##################################################################### proc edit_inputs {} { ## Decimal/floating-point variables. global ENTRYfiltertol ENTRYxmin ENTRYxmax ENTRYymin ENTRYymax ENTRYmarginPcent ## Integer variables. global ENTRYimgMaxSizePx ## Error indicator. global EDITcode ## We could do without the EDITcode variable, by using ## a code with the 'return' statement herein. But using this ## code variable is a little more self-documenting. set EDITcode 0 ####################################################### ## Remove trailing and leading blanks (if any) from the ## user entries in the 'entry' widgets. ####################################################### set ENTRYfiltertol [string trim $ENTRYfiltertol] set ENTRYxmin [string trim $ENTRYxmin] set ENTRYxmax [string trim $ENTRYxmax] set ENTRYymin [string trim $ENTRYymin] set ENTRYymax [string trim $ENTRYymax] set ENTRYmarginPcent [string trim $ENTRYmarginPcent] set ENTRYimgMaxSizePx [string trim $ENTRYimgMaxSizePx] ######################################################################### ## Check that these entry fields are NOT blank. ######################################################################### set MSGblank "is blank. Must NOT be blank." if {"$ENTRYfiltertol" == ""} { popup_msgVarWithScroll .topErr "The 'Filter Precision' parameter $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYxmin" == ""} { popup_msgVarWithScroll .topErr "The X-MIN parameter $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYxmax" == ""} { popup_msgVarWithScroll .topErr "The X-MAX parameter $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYymin" == ""} { popup_msgVarWithScroll .topErr "The Y-MIN parameter $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYymax" == ""} { popup_msgVarWithScroll .topErr "The Y-MAX parameter $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYmarginPcent" == ""} { popup_msgVarWithScroll .topErr "The Margin Percent parameter $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYimgMaxSizePx" == ""} { popup_msgVarWithScroll .topErr "The 'Max Image Size' entry $MSGblank" +10+10 set EDITcode 1 return } ############################################################# ## Check that ENTRYimgMaxSizePx is an integer. ############################################################# set MSGnotInteger " is NOT INTEGER." if {![string is integer -strict "$ENTRYimgMaxSizePx"]} { popup_msgVarWithScroll .topErr "The size of the 'Max Image Size' entry $MSGnotInteger" +10+10 set EDITcode 1 return } ######################################################################### ## Check that ENTRYxmin ENTRYxmax ENTRYymin ENTRYymin ENTRYfiltertol ## are decimal numbers (positive or negative) ## --- such as 1.234 or -3 or -3.0 or -.4 or .5 or 7 ######################################################################### ## Implemented using the 'decimal_check' proc below. ######################################################################### set NUMERICmsg "should be a decimal number. Examples: 1.234 or 0.56 or -.789" if {![decimal_check "$ENTRYfiltertol"]} { popup_msgVarWithScroll .topErr "The 'Filter-Precision' parameter $NUMERICmsg" +10+10 set EDITcode 1 return } if {![decimal_check "$ENTRYxmin"]} { popup_msgVarWithScroll .topErr "The X-MIN parameter $NUMERICmsg" +10+10 set EDITcode 1 return } if {![decimal_check "$ENTRYxmax"]} { popup_msgVarWithScroll .topErr "The X-MAX parameter $NUMERICmsg" +10+10 set EDITcode 1 return } if {![decimal_check "$ENTRYymin"]} { popup_msgVarWithScroll .topErr "The Y-MIN parameter $NUMERICmsg" +10+10 set EDITcode 1 return } if {![decimal_check "$ENTRYymax"]} { popup_msgVarWithScroll .topErr "The Y-MAX parameter $NUMERICmsg" +10+10 set EDITcode 1 return } if {![decimal_check "$ENTRYmarginPcent"]} { popup_msgVarWithScroll .topErr "The Margin Percent parameter $NUMERICmsg" +10+10 set EDITcode 1 return } ####################################################################### ## Check that ENTRYfiltertol and ENTRYmarginPcent and ## ENTRYimgMaxSizePx are not negative. ####################################################################### set POSITIVEmsg "should be a POSITIVE number." if {$ENTRYfiltertol < 0.0} { popup_msgVarWithScroll .topErr "The 'Filter Precision' value $POSITIVEmsg" +10+10 set EDITcode 1 return } if {$ENTRYmarginPcent < 0.0} { popup_msgVarWithScroll .topErr "The Margin Percent value $POSITIVEmsg" +10+10 set EDITcode 1 return } if {$ENTRYimgMaxSizePx < 0} { popup_msgVarWithScroll .topErr "The 'Max Image Size' entry $POSITIVEmsg" +10+10 set EDITcode 1 return } } ## END OF PROC 'edit_inputs' ##+######################################################################## ## PROC: 'decimal_check' ##+######################################################################## ## PURPOSE: Returns 1 or 0 if the input string looks like a decimal number ## --- positive or negative. Example numbers that are OK: ## 1.234 12.34 0.234 .234 6 ## -1.234 -12.34 -0.234 -.234 -6 ##+######################################################################## ## References (lots of one-liners): ## http://stackoverflow.com/questions/2072222/regular-expression-for-positive-and-a-negative-decimal-value-in-java ## http://stackoverflow.com/questions/308122/simple-regular-expression-for-a-decimal-with-a-precision-of-2 ## http://stackoverflow.com/questions/4246077/matching-numbers-with-regular-expressions-only-digits-and-commas/4247184#4247184 ## ## More specific to Tcl-Tk (including multi-liners): ## http://wiki.tcl.tk/989 'Regular Expression Examples' ## http://wiki.tcl.tk/768 'Entry Validation' - See "Integer forbidding leading zero:" ## http://wiki.tcl.tk/10166 'string is' ## http://wiki.tcl.tk/40710 'significant digits rounding' - uses regexp to split a number - ## Splits using: if {[regexp {^([+,-]?)([0-9]+)(\.?[0-9]*)?([eE][+-]?[0-9]+)?$} $num -> s i d e]} ## Removes leading zero with: regexp {^(0*)([1-9][0-9]*)$} $i -> NULL DIG ## http://wiki.tcl.tk/530 'Unit converter' has a regexp to parse numbers: ## set RE {(?ix) # Ignore case, extended syntax ## ([-+]?) # Optional leading sign ## ([0-9]*) # Integer part ## \.? # Optional decimal point ## ([0-9]*) # Fractional part ## (e?[0-9]*) # Optional exponent ## } ## ##+######################################################################## ## I do not mind incurring a little (minute amount of) processing ## with a multiple-line implementation. Probably easier to fix if ## a string gets through --- such as ".0.3" (two decimal points). ## ## CALLED BY: proc 'edit_inputs' ##+######################################################################## proc decimal_check {string} { set PosDecimalOK [regexp {^([0-9]*)\.?([0-9]*)$} "$string"] set NegDecimalOK [regexp {^\-([0-9]*)\.?([0-9]*)$} "$string"] set PosNakedDecimalOK [regexp {^\.?([0-9]*)$} "$string"] set NegNakedDecimalOK [regexp {^\-\.?([0-9]*)$} "$string"] set IntegerOK [string is integer $string] set retCODE [expr { \ $PosDecimalOK || $NegDecimalOK || \ $PosNakedDecimalOK || $NegNakedDecimalOK || \ $IntegerOK }] ## FOR TESTING: if {0} { puts "" puts "decimal_check:" puts "string: $string" puts "PosDecimalOK: $PosDecimalOK" puts "NegDecimalOK: $NegDecimalOK" puts "PosNakedDecimalOK: $PosNakedDecimalOK" puts "NegNakedDecimalOK: $NegNakedDecimalOK" puts "IntegerOK: $IntegerOK" puts "retCODE: $retCODE" } return $retCODE } ## END OF PROC 'decimal_check' ##+######################################################################## ## PROC: 'numeric_check_string_double' ##+######################################################################## ## PURPOSE: To check whether a number/string is a valid floating-point ## number by using the Tcl 'string is double' command. ## ## CALLED BY: proc 'read_file' ##+######################################################################## proc numeric_check_string_double {str2chk} { global EDITcode set EDITcode 0 if {[string is double -strict "$str2chk"] == 0} { popup_msgVarWithScroll .topErr "The string '$str2chk' is not a floating point number. Data error?" +10+10 set EDITcode 1 return } } ## END OF PROC 'numeric_check_string_double' ##+######################################################################## ## PROC: 'writePSfile' ##+######################################################################## ## PURPOSE: Write a Postscript file from the image on the canvas. ## ## CALLED BY: the 'WritePSfile' button ##+######################################################################## proc writePSfile {} { global DIRtemp env ENTRYfilename CNTdatalinesToPlot PSviewer \ newImgWidthPx newImgHeightPx ################################################################## ## If the filename field is empty, bail out of this proc. ################################################################## if {"$ENTRYfilename" == ""} { advise_user "** There is NO FILENAME specified --- probably no data to plot. **" return } ################################################################## ## If the count of lines read does not exist or if the count ## is less than 1, bail out of this proc. ################################################################## if {[info exists CNTdatalinesToPlot] == 0} { advise_user "** It appears that the file DATA HAS NOT BEEN LOADED. **" return } if {$CNTdatalinesToPlot < 1} { advise_user \ "** It appears that NO DATA in the file SATISFIED the LOAD CRITERIA. **" return } ########################################################## ## Set a name for the output file and write the Postscript ## file from the canvas contents. ########################################################## set OUTfile "$DIRtemp/$env(USER)_outline.ps" ## FOR TESTING: if {0} { puts "" puts "PROC 'writePSfile':" puts " OUTfile: $OUTfile" } set err [.fRcanvas.can postscript -file "$OUTfile" \ -colormode color \ -width $newImgWidthPx \ -height $newImgHeightPx \ -x 0 -y 0 \ -rotate false \ -pageheight 11.0i \ -pagewidth 8.5i \ -pagey 0.5i \ -pagex 0.5i \ -pageanchor sw] ## Tk canvas 'postscript' option NOTES: ## ## Color modes: color, gray, mono ## ## It is important to use the '-width' and -height' parms to ## give the size of the image in pixels --- otherwise the ## image may be clipped or positioned improperly on the 'page'. if {"$err" == ""} { advise_user \ "** Postscript file written to file $OUTfile - Started viewer $PSviewer **" exec $PSviewer "$OUTfile" & } else { advise_user \ "** Postscript write may have FAILED. Message: $err **" } } ## END OF PROC 'writePSfile' ##+######################################################################## ## PROC: 'popup_msgVarWithScroll' ##+######################################################################## ## PURPOSE: Report help or error conditions to the user. ## ## We do not use focus,grab,tkwait in this proc, ## because we use it to show help when the GUI is idle, ## and we may want the user to be able to keep the Help ## window open while doing some other things with the GUI ## such as putting a filename in the filename entry field ## or clicking on a radiobutton. ## ## For a similar proc with focus-grab-tkwait added, ## see the proc 'popup_msgVarWithScroll_wait' in a ## 3DterrainGeneratorExaminer Tk script. ## ## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk', ## 4th edition, by Welch, Jones, Hobbs. ## ## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg) ## and a variable holding text (many lines, if needed). ## ## CALLED BY: 'help' button ##+######################################################################## ## To have more control over the formatting of the message (esp. ## words per line), we use this 'toplevel-text' method, ## rather than the 'tk_dialog' method -- like on page 574 of the book ## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications ## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor". ##+######################################################################## proc popup_msgVarWithScroll { toplevName VARtext ULloc} { ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global. ## global env # bell # bell ################################################# ## Set VARwidth & VARheight from $VARtext. ################################################# ## To get VARheight, ## split at '\n' (newlines) and count 'lines'. ################################################# set VARlist [ split $VARtext "\n" ] ## For testing: # puts "VARlist: $VARlist" set VARheight [ llength $VARlist ] ## For testing: # puts "VARheight: $VARheight" ################################################# ## To get VARwidth, ## loop through the 'lines' getting length ## of each; save max. ################################################# set VARwidth 0 ############################################# ## LOOK AT EACH LINE IN THE LIST. ############################################# foreach line $VARlist { ############################################# ## Get the length of the line. ############################################# set LINEwidth [ string length $line ] if { $LINEwidth > $VARwidth } { set VARwidth $LINEwidth } } ## END OF foreach line $VARlist ## For testing: # puts "VARwidth: $VARwidth" ############################################################### ## NOTE: VARwidth works for a fixed-width font used for the ## text widget ... BUT the programmer may need to be ## careful that the contents of VARtext are all ## countable characters by the 'string length' command. ############################################################### ##################################### ## SETUP 'TOP LEVEL' HELP WINDOW. ##################################### catch {destroy $toplevName} toplevel $toplevName # wm geometry $toplevName 600x400+100+50 # wm geometry $toplevName +100+50 wm geometry $toplevName $ULloc wm title $toplevName "Note" # wm title $toplevName "Note to $env(USER)" wm iconname $toplevName "Note" ##################################### ## In the frame '$toplevName' - ## DEFINE THE TEXT WIDGET and ## its two scrollbars --- and ## DEFINE an OK BUTTON widget. ##################################### if {$VARheight > 10} { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" ## -font fontTEMP_varwidth scrollbar $toplevName.scrolly \ -orient vertical \ -command "$toplevName.text yview" scrollbar $toplevName.scrollx \ -orient horizontal \ -command "$toplevName.text xview" } else { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 ## -font fontTEMP_varwidth } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -command "destroy $toplevName" ############################################### ## PACK *ALL* the widgets in frame '$toplevName'. ############################################### ## Pack the bottom button BEFORE the ## bottom x-scrollbar widget, pack $toplevName.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 if {$VARheight > 10} { ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack $toplevName.scrolly \ -side right \ -anchor center \ -fill y \ -expand 0 ## DO NOT USE '-expand 1' HERE on the Y-scrollbar. ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA. pack $toplevName.scrollx \ -side bottom \ -anchor center \ -fill x \ -expand 0 ## DO NOT USE '-expand 1' HERE on the X-scrollbar. ## THAT KEEPS THE TEXT AREA FROM EXPANDING. pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } else { pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } ################################################ ## Set some 'event' bindings to allow for ## easy scrolling through huge listings. ## is a press of the Page-Down key. ## is a press of the Page-Up key. ## is a press of the Home key ## to go to the top of the listing. ## is a press of the End key ## to go to the bottom of the listing. ## is a press of the Up-arrow key. ## is a press of the Down-arrow key. ################################################ bind $toplevName "$toplevName.text yview scroll +1 page" bind $toplevName "$toplevName.text yview scroll -1 page" bind $toplevName "$toplevName.text see 1.0" bind $toplevName "$toplevName.text see end" bind $toplevName "$toplevName.text yview scroll -1 unit" bind $toplevName "$toplevName.text yview scroll +1 unit" ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## $toplevName.text delete 1.0 end $toplevName.text insert end $VARtext $toplevName.text configure -state disabled } ## END OF PROC 'popup_msgVarWithScroll' ##+######################################################## ## Set the 'HELPtext' var. ##+######################################################## set HELPtext \ " ** HELP for this software application whose purpose is to ** ** 'Read an Outline Data File --- 2 columns of numeric data ** (such as, Longitude-Latitude data in decimal-degrees) ** and Draw the Outline (such as, a boundary map)' ** This Tk GUI script READS a file whose lines contain pairs of x,y coordinates --- such as longitude and latitude pairs, in units of decimal degrees. NOTE: Coordinate-pair data in files for drawing maps is usually provided with LONGITUDE BEFORE LATITUDE. You can think of the LONGITUDE value as providing the X-distance along the equator of a planet, and the LATITUDE value as providing the Y-distance from the equator to a north or south pole. The LONGITUDE angle is usually specified between -180 and +180 degrees --- from a zero longitude (like the longitude line through Greenwich, England on Earth). The LATITUDE angle is usually specified between -90 and +90 degrees, measured from the equator. Furthermore, this script DRAWS the x,y points in a rectangular area of a Tk canvas --- by connecting each point to the previous point with a straight line segment. The GUI allows several options to the user, including: - Color-buttons (three) by which to call an external color-selector GUI with which to specify - a background color (for the canvas) - a color for the line segments - a color for text (a title) that may be put on the canvas. - An entry field for a point-distance 'filter precision' number --- to allow for skipping the plotting of any point that is very close to the previous point. (Use a larger number to filter out more points.) - Four entry fields in which to show the limits of the data that was read --- Xmin, Xmax, Ymin, Ymax --- in 'world coordinate' units. (The plotting is done in 'pixel' units.) ************* NOT ONLY MAPS : ************* Note that the outline data in the file does not have to be longitude-latitude map data. It could be pairs of coordinates that provide an outline of almost any object --- for example, a silouette of a person or an animal or an insect or a fish. OR, the data file could provide an outline of a logo --- or a group of alphanumeric characters in one or more font styles. OR, the data file could provide vertex coordinates of a geometric figure like a pentagon or hexagon or octagon. OR, a more complicated geometric figure could be defined by the points in the file --- such as a fractal-like geometric figure, like the 3rd or 4th level of a 'Koch Snowlake'. In fact, the outline data could depict a snowflake --- or flower petals --- or the outline of a leaf --- or the outline of tree branches. Use your imagination. --- In fact, you could use 2 of the 3 columns of 'vertex' data from an ASCII 3D model file --- such as the 'v' records of a Wavefront OBJ file --- to make plots of the x,y or y,z or x,z coordinate pairs --- which would essentially give 'front', 'top' and 'side' views of the points --- but with line-segments drawn between the points in their sequential order in the file. So this utility could be used to perform lots of experiments with data from columnar data files. *********************** SOURCES OF OUTLINE DATA : *********************** The x,y data in the input file (which MAY be longitude,latitude data) is assumed to be in ASCII format, NOT binary. There are many country/continent/state/county/precinct boundary/outline data files on the internet in *ASCII* format. Examples: - 'GeoJSON' files References: https://en.wikipedia.org/wiki/JSON http://json.org https://en.wikipedia.org/wiki/GeoJSON http://geojson.org - 'KML' and 'KMZ' files (where KML = Keyhole Markup Language) which are used in Google Earth and various GPS devices. References: https://en.wikipedia.org/wiki/Keyhole_Markup_Language http://www.opengeospatial.org/standards/kml/ https://developers.google.com/kml/documentation/?csw=1 There is a lot of XML-like markup language in '.geojson' and and '.kml'/'.kmz' files. ('.kmz' files are compressed '.kml' files.) For input to this utility, the user can take those files and 'clean them up' so that there are only a pair of space-separated (or comma-separated) decimal numbers in each line. The input file can contain a few comment lines indicated by '#' in column one of each comment line --- typically one (or a few) comment lines at the top of the file that describe the contents of the file --- and that, optionally, document the source of the data. In '.kml' files, the x,y data is between and markers. This utility is written to read input files in this very simple, minimal format --- x,y ASCII decimal number pairs --- without any XML-like markup language in the input file. There will be shell scripts in the 'FE Nautilus Scripts' subsystem that extract the x,y coordinate pairs from KML and GeoJSON files --- with relatively few XML-tags left over. Some manual editing can then finish the clean-up job. Many outline-map data files in this simple format are available via 'MAPtools' pages in the 'tkGooies' pages of the www.freedomenv.com web site. ******************** HOW THE DATA IS READ : ******************** We include, in the file-reading proc of this Tk script, the ability to replace any comma character with a space --- so that this utility can read CSV = Comma-Separated-Variable files without having to edit the file to remove the commas. In reading the file, the min and max of the x and y values in the first 2 columns of each data record are determined --- and a margin factor is applied to give an initial rectangle (in 'world coordinates') in which to initially plot the data. Those four automatically-determined Xmin,Xmax,Ymin,Ymax values are presented to the user in four entry fields on the GUI. The user can change a margin-percent value on the GUI in order to adjust the margin around the line segments on the plot. The user can also change an image-size value (in pixels) in order to decrease or increase the size of the plot. --- Note that some geographic information files --- such as country or region files --- may contain separate outline 'loops' --- typically representing islands or lakes in the country or region. Example countries: Argentina, Bahamas, Canada Those loops can be separated in the simple input files of this utility by separating the lines of x,y data by a blank line (or comment line) between the 'loops' of data. This Tk script detects those blank or comment (separator) lines, and uses that as an indication to 'lift the pen' of this plot utility and start plotting the next group of x,y lines as a separate connected 'loop'. ******************** THE 'ADD 360' OPTION : ******************** In some outline files, the longitude values change suddenly from 180 degrees to -180 degrees (as one goes east). When plotting a small part of the Earth (such as near Australia or near Siberia or near the Alaskan Aleutian islands), this can result in a map that has some lines on the far left (near -180) and some lines on the far right (near +180). To avoid this condition, the 'Add 360 to negative longitudes' checkbutton can be checked before doing a 'ReDraw'. This has the effect of plotting the data with x going from 0 to 360 instead of from -180 to +180. (The points between -180 and 0 are translated to 180 to 360.) This can bring far-separated data points together. ******************* CAPTURING THE IMAGE : ******************* This Tk script does not currently provide a 'Write-Image-File' button, but ... The user can use a screen-capture utility, such as 'gnome-screenshot' on Linux, to capture an image of any map that is drawn --- say in a PNG file. The PNG file could be converted to a GIF or JPEG file --- say with the ImageMagick 'convert' command on Linux. An image editor, such as 'mtpaint' on Linux, could be used to crop the image file that is captured --- and perform other image processing options. For example, a map image with a black background could be 'color-inverted' to yield a map with a white background --- if you forgot to use the 'Color' buttons of the GUI to make tne background of the image white and the line and text suitable darker colors. (An image with white background could save a lot of ink when printing out the image to paper --- using the Print option of an image-editor or image-viewer or web-browser or word-processor.) *************** USING THE IMAGE : *************** The captured image file could be read into one of the other FE 'tkGooie' utilities --- such as 'IMAGEtools' 'tkGooies' --- to apply more objects (text, lines, images) to the image. Example 'tkGooies': - the 'Make Title Block' utility - the 'wheeDiagram' utility - the 'Sketch On Image' utility - the 'Rotate Image' utility. - the 'Merge 2 Images' utility - the 'wheeeMorph' utility OR, use a light-weight' (fast-starting) image editor utility, such as 'mtpaint' on Linux, to add text or lines to the image --- or perform other operations including cropping and color editing. OR, use a 'monolithic' image editing utility such as 'Gimp' or 'Inkscape' to add features to the image. " ##+##################################################### ##+##################################################### ## ADDITIONAL GUI INITIALIZATION, if needed (or wanted). ##+##################################################### ##+##################################################### ##+##################################################### ## Set the full-name of the RGB color-selector Tk script ## that is used in several procs above. ##+##################################################### ## FOR TESTING: # puts "argv0: $argv0" set DIRthisScript "[file dirname $argv0]" ## For ease of testing in a Linux/Unix terminal and located at the ## directory containing this Tk script. Set the full directory name. if {"$DIRthisScript" == "."} { set DIRthisScript "[pwd]" set CURdir "[pwd]" } set DIRupOne "[file dirname "$DIRthisScript"]" set DIRupTwo "[file dirname "$DIRupOne"]" set ColorSelectorScript "$DIRupTwo/SELECTORtools/tkRGBselector/sho_colorvals_via_sliders3rgb.tk" ## Alternatively: Put the RGB color-selector Tk script in the ## same directory as this Tk script and uncomment the following. # set ColorSelectorScript "$DIRthisScript/sho_colorvals_via_sliders3rgb.tk" ##+############################################################# ## Set an initial directory location CURdir for the 'get_filename' ## proc --- an initial directory in which to look for boundary-data ## filenames. ##+############################################################## ## For ease of testing in a Linux/Unix terminal whose 'current ## working directory' is located at the directory containing this ## Tk script (and a test data file), we set CURdir to 'pwd' if ## it looks like this script is being run in a terminal-testing ## mode. ## ## Otherwise, we set CURdir to the user home directory. ##+############################################################## set CURdir "$env(HOME)" if {"$DIRthisScript" == "[pwd]"} { set CURdir "[pwd]" } ##+############################################################## ## Set a directory for output, such as a Postscript plot file. ##+############################################################## set DIRtemp "/tmp" ##+############################################# ## Initialize the canvas-background-color. ## ## (Change 'if {1}' to 'if {0}' to try an ## alternative min-color.) ##+############################################# if {0} { ## Dark Blue: set COLOR0r 0 set COLOR0g 0 set COLOR0b 170 } else { ## White: set COLOR0r 255 set COLOR0g 255 set COLOR0b 255 } set COLOR0hex [format "#%02X%02X%02X" $COLOR0r $COLOR0g $COLOR0b] update_color_buttons "color0" ##+############################################# ## Initialize the canvas-line-segments-color. ## ## (Change 'if {1}' to 'if {0}' to try an ## alternative canvas-line-segment-color.) ##+############################################# if {0} { ## Yellow: set COLOR1r 255 set COLOR1g 255 set COLOR1b 0 } else { ## Black: set COLOR1r 0 set COLOR1g 0 set COLOR1b 0 } set COLOR1hex [format "#%02X%02X%02X" $COLOR1r $COLOR1g $COLOR1b] update_color_buttons "color1" ##+############################################# ## Initialize the canvas-text-color. ## ## (Change 'if {1}' to 'if {0}' to try an ## alternative canvas-text-color.) ##+############################################# if {0} { ## Orange: set COLOR2r 255 set COLOR2g 200 set COLOR2b 0 } else { ## Red: set COLOR2r 255 set COLOR2g 0 set COLOR2b 0 } set COLOR2hex [format "#%02X%02X%02X" $COLOR2r $COLOR2g $COLOR2b] update_color_buttons "color2" #+####################################################### ## Set the initial/default 'plot type' to use. #+####################################################### set VARdrawtype "outline" # set VARdrawtype "fill" #+####################################################### ## Set a viewer for Postscript files. #+####################################################### # set PSviewer "/usr/bin/display" # set PSviewer "/usr/bin/psview" # set PSviewer "/usr/bin/gsview" # set PSviewer "/usr/bin/gs -q -dBATCH" # set PSviewer "/usr/bin/xpdf" set PSviewer "/usr/bin/evince" #+####################################################### ## Initialize the entry widgets on the GUI. ##+###################################################### reset_parms ##+##################################################### ## Advise the user how to start. ##+##################################################### advise_user "$aRtext(SELECT-LOAD-DRAWmsg)"