######################
#
# FLOWPATHxml.SML
#
# Demonstration SML ToolScript using an XML specification to create the
# toolscript dialog window.
#
# Requires TNTmips Version 7.0
# Get the lastest version of this toolscript from our website www.microimages.com
#####################
# View ToolScript
#
# 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
#
# To use this ToolScript add one or more DEM's to your Group Display
# The DEM you actually want to use in the watershed process should be the first layer in the group
# i.e. the layer at the bottom of the Group Controls window if you want to change the DEM that you were using
# switch from the ToolScript tool to some other tool and move that DEM to the bottom of the Group Controls
# and reactivate the Toolscript it will ask if you want to switch the input DEM
# you can run several successive flowpaths and buffer zones keeping the same DEM
array seedx[10];
array seedy[10];
class WATERSHED w;
numeric numpts, i;
class RASTER DEM;
numeric firstpass;
class VECTORLAYER VecBuf;
class VECTORLAYER VecFlow;
class VECTORLAYER BasinLayer;
class VECTORLAYER BoundaryLayer;
class RasterLayer DEMLayer;
numeric haslayers;
class XMLDOC doc;
class XMLNODE dlgnode;
class GUI_DLG dlgform;
class GUI_CTRL_COLORBUTTON bocolorbtn, fcolorbtn, bacolorbtn, bucolorbtn;
numeric setDefaultWhenClose;
class VECTOR VectIn;
class VECTOR VecBoundary;
class POINT2D pt;
array xPoints[10],yPoints[10];
numeric xMax,yMax,xMin,yMin;
array xhold[10], yhold[10];
class PromptNum PromptDistance;
class VECTOR VECFLOW, VECFLOW2;
string tempfilename$, demFilename$, demObjname$;
string flowpathFilename$, flowpathObjname$;
string userflowpathFilename$, userflowpathObjname$;
string userBasinFilename$, userBasinObjname$;
numeric tempinode, demInode, flowpathInode;
string tempobjname$;
class Color bocolor;
class Color fcolor;
class Color bucolor;
class Color bacolor;
# function to create a status to attach popup messages to
# the advantage is that if the process is quick dialog won't pop in/out
# requires tntdisp 20001012 or later
proc StatusStart() {
class StatusHandle status;
class StatusContext context;
status = StatusDialogCreate();
context = StatusContextCreate(status);
}
proc StatusStop() {
StatusContextDestroy(context);
StatusDialogDestroy(status);
}
# called when user presses save button on dialog
# if something wasn't calculated by choice output object is empty
proc DoSave() {
local numeric answer, destParentInode, userflowpathInode, userbasinInode;
local string destFilename$;
local vector Buffer2;
if (haslayers) {
answer = PopupYesNo("Save Output Objects?");
if (answer) {
#Get output Filename
destFilename$ = GetOutputFileName(" ","Watershed output","rvc");
CreateProjectFile(destFilename$,"Watershed output");
destParentInode = 0;
userflowpathInode = ObjectNumber(userflowpathFilename$,userflowpathObjname$,"VECTOR");
CopyObject(userflowpathFilename$,userflowpathInode,destFilename$,destParentInode);
CreateVector(Buffer2,destFilename$,"Buffer","Flow path buffer zone","Polygonal");
Buffer2 = VectorToBufferZone(VectIn,"line",PromptDistance.value,"meters");
userbasinInode = ObjectNumber(userBasinFilename$,userBasinObjname$,"VECTOR");
CopyObject(userBasinFilename$,userbasinInode,destFilename$,destParentInode);
}
}
}
# function to remove layers from display
proc DoRemove() {
if (haslayers) {
View.DisableRedraw = 1;
#save user's changes to colors;
fcolor.red = VecFlow.Line.NormalStyle.Color.red;
fcolor.green = VecFlow.Line.NormalStyle.Color.green;
fcolor.blue = VecFlow.Line.NormalStyle.Color.blue;
bucolor.red = VecBuf.Line.NormalStyle.Color.red;
bucolor.green = VecBuf.Line.NormalStyle.Color.green;
bucolor.blue = VecBuf.Line.NormalStyle.Color.blue;
bacolor.red = BasinLayer.Line.NormalStyle.Color.red;
bacolor.green = BasinLayer.Line.NormalStyle.Color.green;
bacolor.blue = BasinLayer.Line.NormalStyle.Color.blue;
bocolor.red = BoundaryLayer.Line.NormalStyle.Color.red;
bocolor.green = BoundaryLayer.Line.NormalStyle.Color.green;
bocolor.blue = BoundaryLayer.Line.NormalStyle.Color.blue;
VecFlow = LayerDestroy(VecFlow);
VecBuf = LayerDestroy(VecBuf);
BasinLayer = LayerDestroy(BasinLayer);
View.DisableRedraw = 0;
haslayers = 0;
}
}
# called when user presses remove button on dialog
proc cbDoRemove() {
DoRemove();
ViewRedrawIfNeeded(View);
}
# called when the user presses the set button on dialog
# sets the number of seedpoints to use
proc DoSet() {
numpts = PopupNum("Enter number of seed points", numpts,1,10);
i = 1;
}
# Close the window, switching to default tool
# called when user presses close button and when clicks x in dialog
proc DoClose() {
if (setDefaultWhenClose) {
setDefaultWhenClose = false;
View.SetDefaultTool();
}
}
# Called when new flow path color is selected from toolscript dialog
proc OnChangefcolor () {
fcolor = fcolorbtn.GetColor();
if (GroupGetLayerByName(Group,VecFlow.Name) != 0) {
View.DisableRedraw = 1;
VecFlow.Line.NormalStyle.Color.red = fcolor.red;
VecFlow.Line.NormalStyle.Color.green = fcolor.green;
VecFlow.Line.NormalStyle.Color.blue = fcolor.blue;
View.DisableRedraw = 0;
ViewRedraw(View);
}
}
# Called when new basin color is selected from toolscript dialog
proc OnChangebacolor () {
bacolor = bacolorbtn.GetColor();
if (GroupGetLayerByName(Group,BasinLayer.Name) != 0) {
View.DisableRedraw = 1;
BasinLayer.Line.NormalStyle.Color.red = bacolor.red;
BasinLayer.Line.NormalStyle.Color.green = bacolor.green;
BasinLayer.Line.NormalStyle.Color.blue = bacolor.blue;
View.DisableRedraw = 0;
ViewRedraw(View);
}
}
# Called when new buffer zone color is selected from toolscript dialog
proc OnChangebucolor () {
bucolor = bucolorbtn.GetColor();
if (GroupGetLayerByName(Group,VecBuf.Name) != 0) {
View.DisableRedraw = 1;
VecBuf.Line.NormalStyle.Color.red = bucolor.red;
VecBuf.Line.NormalStyle.Color.green = bucolor.green;
VecBuf.Line.NormalStyle.Color.blue = bucolor.blue;
View.DisableRedraw = 0;
ViewRedraw(View);
}
}
# Called when new extents box color is selected from toolscript dialog
proc OnChangebocolor () {
bocolor = bocolorbtn.GetColor();
# PopupMessage(sprintf("Extents box red = %d, green = %d, blue = %d",bocolor.red,bocolor.green,bocolor.blue))
View.DisableRedraw = 1;
BoundaryLayer.Poly.Select.Mode = "None";
BoundaryLayer.Line.NormalStyle.Color.red = bocolor.red;
BoundaryLayer.Line.NormalStyle.Color.green = bocolor.green;
BoundaryLayer.Line.NormalStyle.Color.blue = bocolor.blue;
View.DisableRedraw = 0;
ViewRedraw(View);
}
# Called the first time the tool is activated.
# If the tool implements a dialog it should be created (but not displayed) here.
func OnInitialize () {
local numeric err;
# set up initial colors for vector layers
bocolor.red = 100;
bocolor.green = 0;
bocolor.blue = 0;
fcolor.red = 0;
fcolor.green = 0;
fcolor.blue = 100;
bucolor.red = 100;
bucolor.green = 100;
bucolor.blue = 0;
bacolor.red = 0;
bacolor.green = 100;
bacolor.blue = 0;
StatusStart();
# get the raster input DEM
if (Group.FirstLayer.Type == "Raster") {
DispGetRasterFromLayer(DEM,Group.FirstLayer);
DEMLayer = Group.FirstLayer;
}
else {
PopupString("First Layer must be a raster object for Watershed Toolscript");
WaitForExit();
}
demFilename$ = GetObjectFileName(DEM);
demInode = GetObjectNumber(DEM);
demObjname$ = GetObjectName(demFilename$,demInode);
# Initialize watershed object (assigns object handle)
w = WatershedInit(demFilename$,demObjname$);
# Fill all depressions in DEM and compute watersheds.
# a depressionless version of the DEM is automatically created
# as a temporary internal object associated with watershed handle w
WatershedCompute(w,"FillAllDepressions,FlowPath");
# get the Flow Paths for the entire raster
WatershedGetObject(w,"VectorFlowPath",flowpathFilename$,flowpathObjname$);
flowpathInode = ObjectNumber(flowpathFilename$,flowpathObjname$,"VECTOR");
CreateTempVector(VECFLOW);
tempfilename$ = GetObjectFileName(VECFLOW);
tempinode = GetObjectNumber(VECFLOW);
tempobjname$ = GetObjectName(tempfilename$,tempinode);
tempinode = CopyObject(flowpathFilename$,flowpathInode,tempfilename$);
tempfilename$ = GetObjectFileName(VECFLOW);
tempobjname$ = GetObjectName(tempfilename$,tempinode);
CloseVector(VECFLOW);
OpenVector(VECFLOW2,tempfilename$,tempobjname$);
firstpass = 1;
haslayers = 0;
numpts = 1;
# read and parse into memory the XML text describing the dialog; return an error code
# if there are syntax errors in the dialog specification.
err = doc.Parse(
'
' );
if (err < 0 ) { # pop up a dialog to report XML syntax errors
PopupError(err);
Exit();
}
# get the dialog element from the parsed XML structure and show error
# message if the dialog element can't be found
dlgnode = doc.GetElementByID("dlgform");
if (dlgnode == 0) {
PopupMessage("Could not find dialog node in XML document");
Exit();
}
# set the XML dialog element as the source for the GUI_DLG class instance
# we are using for the main dialog window, and open as a modeless dialog
dlgform.SetXMLNode(dlgnode);
dlgform.CreateModeless();
# Assign initial colors to color buttons on dialog
fcolorbtn = dlgform.GetCtrlByID("fcolor");
fcolorbtn.SetColor(fcolor);
bacolorbtn = dlgform.GetCtrlByID("bacolor");
bacolorbtn.SetColor(bacolor);
bucolorbtn = dlgform.GetCtrlByID("bucolor");
bucolorbtn.SetColor(bucolor);
bocolorbtn = dlgform.GetCtrlByID("bocolor");
bocolorbtn.SetColor(bocolor);
StatusStop();
} # end of OnInitialize
# procedure to move seed point(s) to nearest flow path(s) before computing
# new watershed vectors
proc DoSnapSeed () {
class VECTOR tempflowvector, TempFlowBuffer;
class VECTOR stub;
numeric linenumber;
numeric a;
numeric b;
numeric returndistance;
numeric tempx;
numeric tempy;
array tempseedx[1];
array tempseedy[1];
CreateTempVector(stub,"VectorToolkit");
VectorToolkitInit(stub);
tempseedx[1] = seedx[i];
tempseedy[1] = seedy[i];
StatusStart();
WatershedComputeElements(w,tempseedx,tempseedy,1,"FlowPath");
StatusStop();
WatershedGetObject(w,"VectorUserFlowPath",userflowpathFilename$,userflowpathObjname$);
OpenVector(tempflowvector,userflowpathFilename$,userflowpathObjname$);
CreateTempVector(TempFlowBuffer);
TempFlowBuffer = VectorToBufferZone(tempflowvector,"line",1,"meters");
stub = VectorExtract(TempFlowBuffer,VECFLOW2,"InsideClip");
# if this vector has no lines then the user defined flowpath runs straight off DEM
# and doesn't connect with the flowpath so skip these calculations, thus leaving
# the seedpoint where it was originally
if (NumVectorLines(stub) > 0) {
string stubfilename$;
string stubobjname$;
numeric stubinode;
stubfilename$ = GetObjectFileName(stub);
stubinode = GetObjectNumber(stub);
stubobjname$ = GetObjectName(stubfilename$,stubinode);
class VECTOR stub2;
CreateTempVector(stub2);
stubfilename$ = GetObjectFileName(stub);
stubinode = GetObjectNumber(stub);
stubobjname$ = GetObjectName(stubfilename$,stubinode);
CloseVector(stub);
OpenVector(stub2,stubfilename$,stubobjname$,"VectorToolkit");
pt.x = xhold[i];
pt.y = yhold[i];
pt = TransPoint2D(pt,ViewGetTransLayerToScreen(View,DEMLayer,1));
pt = TransPoint2D(pt,ViewGetTransLayerToView(View,DEMLayer));
pt = TransPoint2D(pt,ViewGetTransMapToView(View,DEMLayer.projection,1));
MapToObject(GetLastUsedGeorefObject(stub2), pt.x, pt.y, stub2, tempx, tempy);
linenumber = FindClosestLine(stub2,pt.x,pt.y,GetLastUsedGeorefObject(stub2),9999999.9,returndistance);
ClosestPointOnLine(stub2,linenumber,tempx,tempy,a,b);
ObjectToMap(stub2,a,b,GetLastUsedGeorefObject(stub),tempx,tempy);
MapToObject(GetLastUsedGeorefObject(DEM),tempx,tempy,DEM,a,b);
seedx[i] = a;
seedy[i] = b;
}
} # End of DoSnapSeed()
# compute and display flowpath,buffer and buffer zone if chosen by user
proc DoFlowPath() {
StatusStart();
# compute vector flow paths originating at seed point location.
# This step requires the previous computation of the depressionless DEM
#don't draw view until we are done
View.DisableRedraw = 1;
#only calculate what we have to
local btnFlowSet;
local btnBasinSet;
local btnBufferSet;
class GUI_CTRL_TOGGLEBUTTON btnFlow, btnBasin, btnBuffer; # this method works!
btnFlow = dlgform.GetCtrlByID("btnFlow");
btnBasin = dlgform.GetCtrlByID("btnBasin");
btnBuffer = dlgform.GetCtrlByID("btnBuffer");
btnFlowSet = btnFlow.GetValue();
btnBasinSet = btnBasin.GetValue();
btnBufferSet = btnBuffer.GetValue();
########## Popup message for testing data retrieval from dialog
# PopupMessage( sprintf("Flow= %s, Basin= %s, Buffer = %s",btnFlowSet$,btnBasinSet$,btnBufferSet$) )
if ((btnFlowSet == 0) and (btnBufferSet == 0) and (btnBasinSet == 0)) {
return;
}
local string wsopt$;
wsopt$ = "";
if ( (btnFlowSet == 1) or (btnBufferSet ==1)) then wsopt$ = "FlowPath";
if (btnBasinSet == 1) {
if (wsopt$ != "") then wsopt$ = wsopt$ + ",";
wsopt$ = wsopt$ + "Basin";
}
WatershedComputeElements(w,seedx,seedy,numpts,wsopt$);
# if ((btnFlowSet == 1) or (btnBufferSet == 1) and (btnBasinSet == 1)) {
# WatershedComputeElements(w,seedx,seedy,numpts,"FlowPath,Basin");
# }
# if ((btnBasinSet == 1) and (btnBufferSet == 0) and (btnFlowSet == 0)){
# WatershedComputeElements(w,seedx,seedy,numpts,"Basin");
# }
# if (btnBasinSet == 0) {
# WatershedComputeElements(w,seedx,seedy,numpts,"FlowPath");
# }
if ((btnFlowSet == 1) or (btnBufferSet == 1)) {
WatershedGetObject(w,"VectorUserFlowPath",userflowpathFilename$,userflowpathObjname$);
OpenVector(VectIn,userflowpathFilename$,userflowpathObjname$);
}
if (btnFlowSet == 1) { # had to calculate it for buffer now add it if they want it
VecFlow = GroupQuickAddVectorVar(Group,VectIn);
#change the displayed color according to user (previous) choice
VecFlow.Line.NormalStyle.Color.red = fcolor.red;
VecFlow.Line.NormalStyle.Color.green = fcolor.green;
VecFlow.Line.NormalStyle.Color.blue = fcolor.blue;
}
# Compute Buffer zone around flow path
if (btnBufferSet == 1) {
class VECTOR Buffer, TempBuffer;
class GUI_CTRL_EDIT_NUMBER buffDist;
buffDist = dlgform.GetCtrlByID("buffDist");
CreateTempVector(Buffer);
CreateTempVector(TempBuffer);
TempBuffer = VectorToBufferZone(VectIn,"line",buffDist.GetValueNum(),"meters");
Buffer = VectorExtract(VecBoundary,TempBuffer,"InsideClip");
VecBuf = GroupQuickAddVectorVar(Group,Buffer);
VecBuf.Line.NormalStyle.Color.red = bucolor.red;
VecBuf.Line.NormalStyle.Color.green = bucolor.green;
VecBuf.Line.NormalStyle.Color.blue = bucolor.blue;
}
# Display basin if selected
if (btnBasinSet == 1) {
class VECTOR BasinVector;
WatershedGetObject(w,"VectorUserBasin",userBasinFilename$,userBasinObjname$);
OpenVector(BasinVector,userBasinFilename$,userBasinObjname$);
BasinLayer = GroupQuickAddVectorVar(Group,BasinVector);
BasinLayer.Poly.Select.Mode = "None";
BasinLayer.Line.NormalStyle.Color.red = bacolor.red;
BasinLayer.Line.NormalStyle.Color.green = bacolor.green;
BasinLayer.Line.NormalStyle.Color.blue = bacolor.blue;
}
View.DisableRedraw = 0;
ViewRedrawIfNeeded(View);
haslayers = 1;
StatusStop();
} # end of DoFlowPath
# 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.
#don't destroy added layers here since they are already destroyed before this gets called
func OnDestroy () {
WatershedClose(w);
} # end of OnDestroy
# Called when tool is activated.
# If the tool implements a dialog it should be "managed" (displayed) here.
func OnActivate () {
StatusStart();
if (!firstpass) {
local numeric answer;
answer = PopupYesNo("Keep Same Input DEM?");
if (!answer) {
CloseRaster(DEM);
if (Group.FirstLayer.Type == "Raster") {
DispGetRasterFromLayer(DEM,Group.FirstLayer);
DEMLayer = Group.FirstLayer;
}
else {
PopupString("First Layer must be a raster object for Watershed Toolscript");
WaitForExit();
}
demFilename$ = GetObjectFileName(DEM);
demInode = GetObjectNumber(DEM);
demObjname$ = GetObjectName(demFilename$,demInode);
#Initialize watershed object (assigns object handle)
w = WatershedInit(demFilename$,demObjname$);
#Fill all depressions in DEM and compute watersheds.
#a depressionless version of the DEM is automatically created
# as a temporary internal object associated with watershed handle w
WatershedCompute(w,"FillAllDepressions,FlowPath");
#get the Flow Paths for the entire raster
WatershedGetObject(w,"VectorFlowPath",flowpathFilename$,flowpathObjname$);
flowpathInode = ObjectNumber(flowpathFilename$,flowpathObjname$,"VECTOR");
CreateTempVector(VECFLOW);
tempfilename$ = GetObjectFileName(VECFLOW);
tempinode = GetObjectNumber(VECFLOW);
tempobjname$ = GetObjectName(tempfilename$,tempinode);
tempinode = CopyObject(flowpathFilename$,flowpathInode,tempfilename$);
tempfilename$ = GetObjectFileName(VECFLOW);
tempobjname$ = GetObjectName(tempfilename$,tempinode);
CloseVector(VECFLOW);
OpenVector(VECFLOW2,tempfilename$,tempobjname$);
}
}
# draw vector box around DEM so can show and clip buffer zone to it
CreateTempVector(VecBoundary,"VectorToolkit");
local class GEOREF g = GetLastUsedGeorefObject(DEM);
CreateImpliedGeoref(VecBoundary, g.CoordRefSys);
VectorToolkitInit(VecBoundary);
GetObjectExtents(DEM,xMin,yMin,xMax,yMax,GetLastUsedGeorefObject(DEM));
xPoints[1] = xMin;
yPoints[1] = yMin;
xPoints[2] = xMax;
yPoints[2] = yMin;
xPoints[3] = xMax;
yPoints[3] = yMax;
xPoints[4] = xMin;
yPoints[4] = yMax;
xPoints[5] = xMin;
yPoints[5] = yMin;
VectorAddLine(VecBoundary, 5, xPoints, yPoints);
CloseVector(VecBoundary);
View.DisableRedraw = 1;
BoundaryLayer.Poly.Select.Mode = "None";
BoundaryLayer = GroupQuickAddVectorVar(Group,VecBoundary);
BoundaryLayer.IgnoreExtents = 1;
BoundaryLayer.Line.NormalStyle.Color.red = bocolor.red;
BoundaryLayer.Line.NormalStyle.Color.green = bocolor.green;
BoundaryLayer.Line.NormalStyle.Color.blue = bocolor.blue;
View.DisableRedraw = 0;
ViewRedrawIfNeeded(View);
# set i
i = 1;
dlgform.Open();
setDefaultWhenClose = true;
StatusStop();
} # 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 () {
DoRemove();
View.DisableRedraw = 1;
BoundaryLayer = LayerDestroy(BoundaryLayer);
View.DisableRedraw = 0;
ViewRedrawIfNeeded(View);
firstpass = 0;
setDefaultWhenClose = false;
dlgform.Close(0);
} # end of OnDeactivate
# Called when Apply button on dialog is pressed.
proc OnApply () {
DoRemove();
if (i <= numpts) {
# make sure seed points are within bounds of raster
if (seedx[i] > (DEM.$Info.NumCols-1))
seedx[i] = (DEM.$Info.NumCols-1);
if (seedx[i] < 0)
seedx[i] = 0;
if (seedy[i] > (DEM.$Info.NumLins-1))
seedy[i] = (DEM.$Info.NumLins-1);
if (seedy[i] < 0)
seedy[i] = 0;
#if user wants to move seedpoint to flowpath
if (dlgform.GetCtrlValueNum("btnSnap") == 1) {
DoSnapSeed();
}
i = i + 1;
}
if (i > numpts) {
DoFlowPath();
i = 1;
}
}
# Called when user releases 'left' pointer/mouse button.
func OnLeftButtonPress() {
DoRemove();
if (i <= numpts) {
pt.x = PointerX;
pt.y = PointerY;
xhold[i] = PointerX;
yhold[i] = PointerY;
pt = TransPoint2D(pt,ViewGetTransLayerToScreen(View, DEMLayer, 1));
seedx[i] = pt.x;
seedy[i] = pt.y;
# make sure seed points are within bounds of raster
if (seedx[i] > (DEM.$Info.NumCols-1))
seedx[i] = (DEM.$Info.NumCols-1);
if (seedx[i] < 0)
seedx[i] = 0;
if (seedy[i] > (DEM.$Info.NumLins-1))
seedy[i] = (DEM.$Info.NumLins-1);
if (seedy[i] < 0)
seedy[i] = 0;
#if user wants to move seedpoint to flowpath
if (dlgform.GetCtrlValueNum("btnSnap") == 1) {
DoSnapSeed();
}
i = i + 1;
}
if (i > numpts) {
DoFlowPath();
i = 1;
}
} # end of OnLeftButtonPress()