# 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 <shift> key being pressed or 0 if not
# number CtrlPressed 1 if <ctrl> 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