###################### # # 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()