# View ToolScript # This tool opens a Command Parser window that can be used to paint raster cell values. # # To use this tool follow these steps. # 1. Add the compar.sml tool script icon to the View window, # (View window / Options menu / Customize / Tool Scripts). # 2. Create a comma separated value (CSV) file with standard color values. # Either create your own CSV file or, if you have an 8-bit raster that has a color map # you want to use universally, add the raster to the View and enter the 't' command in # the Command Parser window. (Click the Command Parser tool icon to open the Command Parser # window.) CSV file can be edited with a text editor or spreadsheet program. # Example CSV values: # Index Red Green Transparency # 0 0 0 50 # 1 0 100 50 # 2 75 75 50 # . . . . # . . . . # . . . . # 255 0 0 100 # # The CSV file's index indicates a color value when used to paint (p and pr commands) # or a cell value when the CSV file is copied to or from a color palette (t command). # The color component values and transparency values are a percentages that determine # display parameters (any value from 0 to 100 is valid). # You must have an entry for all possible index values: 0 to 255. # # If you want to use this tool to classify an image: # 1. Add the image you want to classify as a reference layer and set the Contrast # enhancement to stretch the cell values for improved display. Add any other # reference layers you want. # 2. Add the same image again and make sure the Contrast is set to None. This image will # look like a class raster when displayed with the new color map you are creating. # The raster cells retain their original values. Make sure this image is the top # layer and select it in the Layer Controls window before you enter any commands. # 3. Click the Command Parser tool icon to open the Command Parser window. # 4. Enter the following commands to get ready to paint: # lc (make a new SMLcolor palette from a CSV file, raster must already have a color map) # b (copy a CSV file into an array used to hold paint colors) # 5. Change class raster's Color Palette to SMLcolor, Contrast to None, and enter the # following command: # r,0,255 (cell value 0 to 255 becomes transparent - layer is now invisible) # 6. Look at the reference layers to find cell values belonging to a class. Use the p and # pr commands to paint cell's a specific color corresponding to the appropriate class. # # # The following symbols are predefined # class VIEW View {use to access the view the tool script is attached to} # class GROUP Group {use to access the group being viewed if the script is run from a group view} # class LAYOUT Layout {use to access the layout being viewed if the script is run from a layout view} # number ToolIsActive Will be 0 if tool is inactive or 1 if tool is active # # The following values are also predefined and are valid when the various On...() # functions are called which deal with pointer and keyboard events. # number PointerX Pointer X coordinate within view in pixels # number PointerY Pointer Y coordinate within view in pixels # number ShiftPressed 1 if key being pressed or 0 if not # number CtrlPressed 1 if key being pressed or 0 if not # number LeftButtonPressed 1 if left pointer button pressed or 0 if not # number RightButtonPressed 1 if right pointer button pressed or 0 if not # number MiddleButtonPressed 1 if middle pointer button pressed or 0 if not # # The following script functions will be called (if used in the script) when # the appropriate action or event occurs as described in the comments before each. # To use a function, uncomment the lines containing the 'func' definition # and ending brace '}' by removing the leftmost '#' on the line and add the # function code between the two lines. #allowable width of command numeric width; #change this number if you want a longer/shorter command field width = 20; #string that seperates commands and parameters string delim; #change this if you want some other string to seperate them delim = ","; #global variables class PromptStr Command; class PromptStr Status; numeric setDefaultWhenClose; numeric i; class RASTER rast; class ColorMap cmap; class Color mycolor; class FILE myfile; array numeric ared[256]; array numeric agreen[256]; array numeric ablue[256]; array numeric atransp[256]; numeric hascolorarray; hascolorarray = 0; proc OrderPizza() { string where; numeric number; #checks to see if the number of paramters (tokens) is correct for this function if (NumberTokens(Command.value,delim) != 3) { PopupMessage("Not enough or too many parameters for OrderPizza function"); return; } Status.value = "OrderPizza function started"; #gets the second parameter off of the commmad prompt string and stores it in a string #note the first token is the code that got you to this function from ParseCommand where = GetToken(Command.value,delim,2); #gets the third parameter off of the command string and stores it in a numeric number = StrToNum(GetToken(Command.value,delim,3)); #example of how to perform parameter checking on arguments if (number > 10) { PopupMessage("You can't eat that many pizzas"); return; # this is "fatal" so return from function } PopupString("You ordered pizzas from",where); PopupNum("You ordered this many",number); Command.value = ""; #clears command prompt letting you reenter same command twice in a row Status.value = "OrderPizza function complete"; } proc OutputColorMap() { if (Group.ActiveLayer.Type == "Raster") { DispGetRasterFromLayer(rast,Group.ActiveLayer); cmap = ColorMapFromRastVar(rast); #if cmap.name is empty then we have no color map to export so exit if (cmap.name == "") { PopupMessage("No ColorMap was found in raster object Paint function aborted\n Use lc command to save a csv file as a colormap in the raster object first\n or create one yourself"); Command.value = ""; Status.value = "OutputColorMap aborted"; return; } myfile = GetOutputTextFile("c:/color.csv","Select file for output:" , "csv"); fprintf(myfile,"%s,%s,%s,%s,%s\n","Index","Red","Green","Blue","Transparency"); Status.value = "Outputting Colormap"; for i = 0 to 255 { mycolor = ColorMapGetColor(cmap,i); #range of RGB values is 0-100 fprintf(myfile,"%d,%d,%d,%d,%d\n",i,round(mycolor.red),round(mycolor.green),round(mycolor.blue),round(mycolor.transp)); } fclose(myfile); Status.value = "Colormap outputted"; Command.value = ""; } else { Status.value = "Active Layer must be a raster w/colormap for this function"; Command.value = ""; } } proc TranspValues() { if (NumberTokens(Command.value,delim) != 3) { Status.value = "Not enough parameters for TranspValues function"; return; } local numeric start; local numeric stop; start = StrToNum(GetToken(Command.value,delim,2)); stop = StrToNum(GetToken(Command.value,delim,3)); if (Group.ActiveLayer.Type != "Raster") { PopupMessage("Active Layer must be a raster object for this function"); Command.value = ""; Status.value = "Active layer must be a raster"; return; } DispGetRasterFromLayer(rast,Group.ActiveLayer); cmap = ColorMapFromRastVar(rast); if (cmap.name == "") { PopupMessage("No ColorMap was found in raster object Paint function aborted\n Use lc command to save a csv file as a colormap in the raster object first\n or create one yourself"); Command.value = ""; Status.value = "TranspValues aborted"; return; } Status.value = "Making cells transparent"; for i = start to stop { mycolor = ColorMapGetColor(cmap,i); mycolor.transp = 100; ColorMapSetColor(cmap,i,mycolor); } ColorMapWriteToRastVar(rast,cmap,cmap.Name,cmap.Desc); #force rasterlayer to reload colormap View.DisableRedraw = 1; LayerDestroy(Group.ActiveLayer); GroupQuickAddRasterVar(Group,rast); Group.ActiveLayer.AllowDeleteLayer = 1; View.DisableRedraw = 0; ViewRedraw(View); Status.value = "Cells made transparent"; Command.value = ""; } proc Paint() { if (NumberTokens(Command.value,delim) != 3) { Status.value = "Not enough parameters for Paint function"; return; } if (!hascolorarray) { PopupMessage("Use code b (SetColorArray function) to load a colormap to use first"); Status.value = "Paint function aborted"; Command.value = ""; return; } local numeric cellvalue; local numeric colornumber; cellvalue = StrToNum(GetToken(Command.value,delim,2)); colornumber = StrToNum(GetToken(Command.value,delim,3)); if (Group.ActiveLayer.Type != "Raster") { PopupMessage("Active Layer must be a raster object for this function"); Command.value = ""; Status.value = "Active layer must be a raster"; return; } DispGetRasterFromLayer(rast,Group.ActiveLayer); cmap = ColorMapFromRastVar(rast); if (cmap.name == "") { PopupMessage("No ColorMap was found in raster object Paint function aborted\n Use lc command to save a csv file as a colormap in the raster object first\n or create one yourself"); Command.value = ""; Status.value = "Paint function aborted aborted"; return; } # mycolor = ColorMapGetColor(cmap,cellvalue); #could read in what there already and only change what is needed #assumes that you reference the color numbers 0-255 the plus one is to get the #correct array entry since SML arrays start at 1 see SetColorArray function mycolor.red = ared[colornumber+1]; mycolor.green = agreen[colornumber+1]; mycolor.blue = ablue[colornumber+1]; mycolor.transp = atransp[colornumber+1]; ColorMapSetColor(cmap,cellvalue,mycolor); ColorMapWriteToRastVar(rast,cmap,cmap.Name,cmap.Desc); #force raster to reload colormap View.DisableRedraw = 1; LayerDestroy(Group.ActiveLayer); GroupQuickAddRasterVar(Group,rast); Group.ActiveLayer.AllowDeleteLayer = 1; View.DisableRedraw = 0; ViewRedraw(View); Status.value = "Cell Painted"; Command.value = ""; } proc PaintRange() { if (NumberTokens(Command.value,delim) != 4) { Status.value = "Not enough parameters for PaintRange function"; return; } if (!hascolorarray) { PopupMessage("Use code b (SetColorArray function) to load a colormap to use first"); Status.value = "PaintRange aborted"; Command.value = ""; return; } local numeric start; local numeric stop; local numeric colornumber; start = StrToNum(GetToken(Command.value,delim,2)); stop = StrToNum(GetToken(Command.value,delim,3)); colornumber = StrToNum(GetToken(Command.value,delim,4)); if (Group.ActiveLayer.Type != "Raster") { PopupMessage("Active Layer must be a raster object for this function"); Command.value = ""; Status.value = "Active layer must be a raster"; return; } DispGetRasterFromLayer(rast,Group.ActiveLayer); cmap = ColorMapFromRastVar(rast); if (cmap.name == "") { PopupMessage("No ColorMap was found in raster object Paint function aborted\n Use lc command to save a csv file as a colormap in the raster object first\n or create one yourself"); Command.value = ""; Status.value = "PaintRange function aborted"; return; } Status.value = "Painting Range"; for i = start to stop { #see Paint function for explanation of array indices mycolor.red = ared[colornumber+1]; mycolor.green = agreen[colornumber+1]; mycolor.blue = ablue[colornumber+1]; mycolor.transp = atransp[colornumber+1]; ColorMapSetColor(cmap,i,mycolor); } ColorMapWriteToRastVar(rast,cmap,cmap.Name,cmap.Desc); #force raster to reload colormap View.DisableRedraw = 1; LayerDestroy(Group.ActiveLayer); GroupQuickAddRasterVar(Group,rast); Group.ActiveLayer.AllowDeleteLayer = 1; View.DisableRedraw = 0; ViewRedraw(View); Status.value = "Cell Range Painted"; Command.value = ""; } proc LoadColorMap() { local string line; myfile = GetInputTextFile("c:/colormap.csv","Select ColorMap file","csv"); line = fgetline$(myfile); #checks to see if the file we are loading is valid if ((NumberTokens(line,",") != 5) || ("Index" != GetToken(line, "," ,1))) { PopupMessage("This does not appear to be a valid colormap csv file\nThe proper format is one row of labels then 256 \nnumeric lines of the form red,green,blue,transp \nwhere red,green,blue,transp are in the range 0-100\nDisplay a raster with a colormap already and use the t command to export the colormap to a file to see how the file should look"); Status.value = "Fatal Error ColorMap load halted"; Command.value = ""; return; } Status.value = "Loading Colormap"; for i = 0 to 255 { line = fgetline$(myfile); #additional checks to see if the file we are loading is valid if ((i > 0) && (StrToNum(GetToken(line, ",",1)) == 0)) { PopupMessage("Bad Index Value Encountered while loading ColorMap file ColorMap load aborted"); Status.value = "Fatal Error ColorMap load halted"; Command.value = ""; return; } mycolor.red = StrToNum(GetToken(line, "," ,2)); #first token is the index value mycolor.green = StrToNum(GetToken(line, "," ,3)); mycolor.blue = StrToNum(GetToken(line, "," ,4)); mycolor.transp = StrToNum(GetToken(line, "," ,5)); ColorMapSetColor(cmap,i,mycolor); } if (Group.ActiveLayer.Type != "Raster") { PopupMessage("Active Layer must be a raster object for this function"); Command.value = ""; Status.value = "Active layer must be a raster"; return; } DispGetRasterFromLayer(rast,Group.ActiveLayer); #writes to new colormap called SMLcolor #other functions assume a colormap is saved underneath the raster and manipulate the last used one ColorMapWriteToRastVar(rast,cmap,"SMLcolor","ColorMap created by SML script"); #have raster layer reload now with colormap object View.DisableRedraw = 1; LayerDestroy(Group.ActiveLayer); GroupQuickAddRasterVar(Group,rast); Group.ActiveLayer.AllowDeleteLayer = 1; View.DisableRedraw = 0; ViewRedraw(View); Status.Value = "ColorMap saved as SMLcolor in raster object"; PopupMessage("Colormap was saved as a SMLcolor colormap under raster object\nyou must select this colormap to see the changes\nif you were displaying the raster with a different colormap\nThis is the only function that behaves this way\nAll other function modify the most recently used colormap"); Command.value = ""; } proc SetColorArray() { local string line; #SML arrays indices start at 1 myfile = GetInputTextFile("c:/colormap.csv","Select ColorMap file","csv"); line = fgetline$(myfile); #check to see if the file format is valid if ((NumberTokens(line,",") != 5) || ("Index" != GetToken(line, "," ,1))) { PopupMessage("This does not appear to be a valid colormap csv file\nThe proper format is one row of labels then 256 \nnumeric lines of the form red,green,blue,transp \nwhere red,green,blue,transp are in the range 0-100\nDisplay a raster with a colormap already and use the t command to export the colormap to a file to see how the file should look"); Status.value = "Fatal Error ColorMap load halted"; Command.value = ""; return; } #sml array indices start at 1 see Paint and PaintRange functions Status.value = "Reading Colormap"; for i = 1 to 256 { line = fgetline$(myfile); #additional checks to see if the file we are loading is valid if ((i > 1)&&(StrToNum(GetToken(line, ",",1)) == 0)) { PopupMessage("Bad Index Value Encountered while loading ColorMap file ColorMap load aborted"); Status.value = "Fatal Error SetColorArray halted"; Command.value = ""; hascolorarray = 0; return; } ared[i] = StrToNum(GetToken(line, "," ,2)); #first token is the index value which we don't need agreen[i] = StrToNum(GetToken(line, "," ,3)); ablue[i] = StrToNum(GetToken(line, "," ,4)); atransp[i] = StrToNum(GetToken(line, "," ,5)); } #variable set so that Paint and PaintRange know that there is an actual colormap in the arrays hascolorarray = 1; Status.value = "Colormap read"; Command.value = ""; } proc ListColors() { if (!hascolorarray) { PopupMessage("Use code b (SetColorArray function) to load a colormap to use first"); return; } string tree$; tree$ = "Interpret as [index] (red value, green value, blue value, transp value)\n\n"; Status.value = "Reading Color Array"; for i = 1 to 256 step 8 { tree$ = tree$ + "[" + NumToStr(i-1) + "]" + " (" + NumToStr(ared[i ]) + "," + NumToStr(agreen[i ]) + "," + NumToStr(ablue[i ]) + "," + NumToStr(atransp[i ]) + ") "; tree$ = tree$ + "[" + NumToStr(i) + "]" + " (" + NumToStr(ared[i+1]) + "," + NumToStr(agreen[i+1]) + "," + NumToStr(ablue[i+1]) + "," + NumToStr(atransp[i+1]) + ") "; tree$ = tree$ + "[" + NumToStr(i+1) + "]" + " (" + NumToStr(ared[i+2]) + "," + NumToStr(agreen[i+2]) + "," + NumToStr(ablue[i+2]) + "," + NumToStr(atransp[i+2]) + ") "; tree$ = tree$ + "[" + NumToStr(i+2) + "]" + " (" + NumToStr(ared[i+3]) + "," + NumToStr(agreen[i+3]) + "," + NumToStr(ablue[i+3]) + "," + NumToStr(atransp[i+3]) + ") "; tree$ = tree$ + "[" + NumToStr(i+3) + "]" + " (" + NumToStr(ared[i+4]) + "," + NumToStr(agreen[i+4]) + "," + NumToStr(ablue[i+4]) + "," + NumToStr(atransp[i+4]) + ") "; tree$ = tree$ + "[" + NumToStr(i+4) + "]" + " (" + NumToStr(ared[i+5]) + "," + NumToStr(agreen[i+5]) + "," + NumToStr(ablue[i+5]) + "," + NumToStr(atransp[i+5]) + ") "; tree$ = tree$ + "[" + NumToStr(i+5) + "]" + " (" + NumToStr(ared[i+6]) + "," + NumToStr(agreen[i+6]) + "," + NumToStr(ablue[i+6]) + "," + NumToStr(atransp[i+6]) + ") "; tree$ = tree$ + "[" + NumToStr(i+6) + "]" + " (" + NumToStr(ared[i+7]) + "," + NumToStr(agreen[i+7]) + "," + NumToStr(ablue[i+7]) + "," + NumToStr(atransp[i+7]) + ")\n"; } Status.value = "Color Array being displayed"; PopupMessage(tree$); Status.value = "Color Array message closed"; Command.value = ""; } proc HelpFunction() { string message; string str1$, str2$, str3$, str4$, str5$, str6$, str7$, str75$, str77$, str8$, str9$, str10$, str11$; str1$ = "This is a list of functions, their purpose and command line syntax"; str2$ = "FUNCTION NAME: PURPOSE: COMMAND LINE SYNTAX:"; str3$ = "OrderPizza Simple Example Function pizza,string,number"; str4$ = "OutputColorMap Output Active Layer's colormap in csv format t"; str5$ = "TranspValues Sets cellvalue range colormap entries transparent r,cellvaluestart,cellvaluestop"; str6$ = "Paint Sets cellvalue colormap entry to index array's color value p,cellvalue,index See SetColorArray"; str7$ = "PaintRange Sets cellvalue range colormap entries to index array's color value pr,cellvaluestart,cellvaluestop,index"; str75$ = "Paint and PaintRange functions assume a ColorMap exists in raster object and modifies the last one used"; str77$ = "The functions do not modify your view parameters i.e. if you are display the raster with no colormap you are still displaying the raster with no colormap"; str8$ = "LoadColorMap Inputs a csv file and saves it as a colormap under the raster lc"; str9$ = "SetColorArray Reads in a csv file and stores it a red,green,blue transp array b Used by Paint and PaintRange functions"; str10$= "ListColors Displays the values currently set in the color array l"; str11$ ="HelpFunction Displays this message help or click help button"; # if you add new functions and want to document them add a strx$ variable like this #str12$ = "my new function Does what my new function does my new function's syntax"; #then add a \n%s to this and add the new string variable at the end message = sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", str1$, str2$, str3$, str4$, str5$, str6$, str7$, str75$, str77$, str8$, str9$, str10$, str11$); Status.value = "Help Open"; PopupMessage(message); Command.value = ""; Status.value = "Help Closed"; } proc ParseCommand() { string code; code = GetToken(Command.value,delim,1); numeric valid; valid = 0; #to add new functionality to this script add an if statement of this form #if (code == "the new code you want") { #then call your func or proc here or define the function here #set valid to 1 if you want the script to tell you if it didn't find a function for your code #} if (code == "pizza") { OrderPizza(); valid = 1; } if (code == "t") { OutputColorMap(); valid = 1; } if (code == "r") { TranspValues(); valid = 1; } if (code == "p") { Paint(); valid = 1; } if (code == "pr") { PaintRange(); valid = 1; } if (code == "lc") { LoadColorMap(); valid = 1; } if (code == "b") { SetColorArray(); valid = 1; } if (code == "help") { HelpFunction(); valid = 1; } if (code == "l") { ListColors(); valid = 1; } if (valid == 0) { Status.value = ""; Status.value ="Command not recognized"; } } #callback for [x] button on dialog #switches to default tool which makes on OnDeactivate get called proc DoClose() { if (setDefaultWhenClose) { setDefaultWhenClose = false; View.SetDefaultTool(); } } # Called the first time the tool is activated. # If the tool implements a dialog it should be created (but not displayed) here. func OnInitialize () { class XmForm dlgform; class PushButtonItem btnItemHelp; dlgform = CreateFormDialog("Command Parser",View.Form); WidgetAddCallback(dlgform.Shell.PopdownCallback,DoClose); Command = CreatePromptStr(dlgform,"Enter Command:",width,"Type help for help"); Status = CreatePromptStr(dlgform,"Status:",width+13,"Waiting"); btnItemHelp = CreatePushButtonItem("Click Here for Help",HelpFunction); btnItemHelp.IconName = "help"; class XmRowColumn btnrowaction; btnrowaction = CreateIconButtonRow(dlgform,btnItemHelp); btnrowaction.TopWidget = dlgform; btnrowaction.RightWidget = Command; btnrowaction.LeftWidget = dlgform; btnrowaction.BottomWidget = Status; Command.TopWidget = dlgform; Command.RightWidget = dlgform; Status.LeftWidget = dlgform; Status.BottomWidget = dlgform; Status.TopWidget = Command; #setup so when value in the PromptString Command changes ParseCommand will be called WidgetAddCallback(Command.ValueChangedCallback,ParseCommand); #global variables numeric setDefaultWhenClose; numeric i; class RASTER rast; class ColorMap cmap; class Color mycolor; class FILE myfile; array ared[256]; array agreen[256]; array ablue[256]; array atransp[256]; numeric hascolorarray; hascolorarray = 0; } # end of OnInitialize # Called when tool is to be destroyed, will not be called if tool was never activated. # If the tool implements a dialog it should be destroyed here. func OnDestroy () { DestroyWidget(dlgform); } # end of OnDestroy # Called when tool is activated. # If the tool implements a dialog it should be "managed" (displayed) here. func OnActivate () { dlgform.managed = 1; setDefaultWhenClose = true; } # end of OnActivate # Called when tool is deactivated (usually when switching to another tool). # If the tool implements a dialog it should be "unmanaged" (hidden) here. func OnDeactivate () { setDefaultWhenClose = false; dlgform.managed = 0; } # end of OnDeactivate # Called when tool is to be 'suspended' during a redraw operation. # func OnSuspend () { # } # end of OnSuspend # Called when tool is to be 'resumed' after a redraw operation. # If the tool displays any graphics they should be updated by this function. # func OnResume () { # } # end of OnResume # Called when user presses 'left' pointer/mouse button. # func OnLeftButtonPress () { # } # end of OnLeftButtonPress # Called when user presses 'right' pointer/mouse button. # func OnRightButtonPress () { # } # end of OnRightButtonPress # Called when user presses 'middle' pointer/mouse button. # func OnMiddleButtonPress () { # } # end of OnMiddleButtonPress # Called when user releases 'left' pointer/mouse button. # func OnLeftButtonRelease () { # } # end of OnLeftButtonRelease # Called when user releases 'right' pointer/mouse button. # func OnRightButtonRelease () { # } # end of OnRightButtonRelease # Called when user releases 'middle' pointer/mouse button. # func OnMiddleButtonRelease () { # } # end of OnMiddleButtonRelease # Called when user moves cursor if no button being pressed # func OnPointerMoveNoButton () { # } # end of OnPointerMoveNoButton # Called when user moves cursor while holding down button # func OnPointerMoveWithButton () { # } # end of OnPointerMoveWithButton # Called when cursor enters window associated with view. # func OnEnterWindow () { # } # end of OnEnterWindow # Called when cursor leaves window associated with view. # func OnLeaveWindow () { # } # end of OnLeaveWindow # Called when user presses 'key' on keyboard. #func OnKeyPress (key) { #} # end of OnKeyPress