SampleCatchments.sml

  Download

More scripts: Advanced

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
######################################################################################################
#
# SampleCatchments.sml  (Stand Alone)
#
######################################################################################################
#
# Author:
#    Dave Breitwisch, MicroImages, Inc.
#
# Started:
#    19 September 2005
#
# Updated:
# 9 December 2011 - Fixed problem evaluating unattached basins in series
# 21 November 2011 - Increased size of array used while evaluating unattached basins
# 11 November 2011 - Fix checking flowdirs raster to fix unattached basins
#
# Requires:  TNTmips2005:72 or later
#
numeric version = round(_context.Version * 10);
if (version < 72) {
	PopupMessage("This version requires that you are using TNTmips2006:72 or later");
	Exit();
}
#
######################################################################################################
#
# Documentation:  SampleCatchments.sml
#
# Purpose:
#
#  The purpose of this Stand Alone SML script is to process multiple vector points representing stream or
#    stream sediment samples to compute the upstream catchment (basin) area that drains to each point.  Script inputs 
#    include an elevation raster (DEM) of the area and a vector object containing the sample points with attached attributes.  
#  The script uses data structures and methods associated with the TNTmips Watershed process.
#  
#  Script outputs include:
#    1) a vector object containing standard flowpaths determined from the elevation model   
#    2) a vector object containing the sample points relocated to lie on the nearest flowpath (if points
#          were within the user-specified tolerance distance)
#    3) a vector object containing one catchment area for each relocated sample point.  Each catchment is 
#          represented by one or more polygon elements.  Sample point attributes and computed catchment attributes
#          are attached to all associated catchment polygons.  Multiple sample points within a single watershed 
#          result in multiple catchment areas.  Catchment attributes track the identity of the immediately adjacent
#          upstream samples/catchments and the total area of all contributing catchments.    
#    4) optional vector objects containing
#          a) upstream flowpaths from each sample point
#          b) downstream flowpaths from each sample point
#          c) standard catchment basins for the entire area
#          d) standard ridges for the entire area
#
#  The results from this process can then be analyzed based on the geochemical or other sample data attached 
#  to the sample points.
#
#  The DEM used as input can be either a standard DEM or an adjusted (depressionless) DEM produced by processing
#  a standard DEM using the TNTmips Watershed process.  For most efficient processing of many sample points,
#  first use the Watershed process to create a depressionless DEM and to determine flowpath and basin parameters
#  that create a standard flowpath network that covers the input sample set with the minimum density and complexity. 
#  If using a depressionless DEM as input, turn off the Fill All Depressions toggle on the main dialog window.
#
# Main Dialog:
#
#  Input DEM... - Press this button to open up a selection dialog and select the DEM Raster to be used for the watershed process.
#
#  Input Vector... - Press this button to open a selection dialog and select the Vector that contains a point database with 
#    the geochemical samples.  The table should be named "TABLE" and the unique sample ID field should be labeled "SAMP_NO".  
#    Should the database have different names, the script can be altered on line 72 and 73 to match the data if necessary.
#
#    Sample Table Name - When selecting an input vector, it will prompt for the user to select the database table for that vector
#      that contains the stream sediment samples
#
#    Unique ID Field - Prompted for as above for user to specify the field name that contains the unique sample ID values.
#
#  Watershed Options -
#
#    General - This page allows the user to specify which object will be generated by the script.  Those that are grayed out, are
#      are either required or not available at the time this script is published.
#
#    Mask - This option lets the user select a mask raster for watershed process to use.
#
#    Flow Path and Basin - These settings are the same as those in the Watershed process in TNTmips.  These allow the user
#      to specify how the standard watershed objects are created.  For more details on how to use these settings, see the
#      "Modeling Watershed Geomorphology" tutorial booklet that is available with your TNTmips installation materials or on the
#      MicroImages website.
#
#  Snap to closest flowpath toggle - This option is currently always active.  Although the user can still specify the maximum
#    distance in which to search for the closest standard flowpath line.  If there is not a standard flowpath line within the
#    specified distance, the user is given a warning and the point is not added to the resulting vector and analysis results.
#
#  Snap to downstream node toggle - This option is used to move sample points to the downstream node of the closest line if 
#    it's within the specified distance.  The purpose of this option is to correct a situation in which a sample point may be 
#    taken downstream of the intersection of two streams in the field, but the "flow path" generated from the DEM Raster ends 
#    up with a model where the point will be moved to one of the upstream lines above the intersection.  As a result of this 
#    situation, only one upstream line is sampled.  By using this option, the script will search within the specified search 
#    distance downstream for an intersection of another line.  If found, the sample point moved to this node and will be marked
#    in the database as snapped to a particular node ("SnappedToNode" field).  The watershed process and the other computations
#    of this script will then sample everything upstream of this node.
#
#  Output Project File... - Press this button to open a selection dialog and create a new project file for the output objects
#    of the script to be created in.
#
#  Output Summary File... - Press this button to open a selection dialog and select a new text file.  A summary of certain 
#    steps, warnings and corrections are printed to both the SML console window and this file.  This is an extra tool for the 
#    user to look through to help evaluate the results of the script.
#
#
# Output RVC Objects Created By The Script:
#
#  The following objects (alphabetical order) are created in the project file created when the "Output Project File..." button was pressed.
#
#  Result_Vector - This vector is created with a copy of the point database table that is in the Vector selected when the 
#    "Input Vector..." button was pressed.  After the STDFLOWPATH Vector has been created, each sample point is moved to the 
#    closest line and then to the center of the DEM Raster cell on that line (all flow paths are computed to go through the 
#    center of the cells).  This point is added to the vector and attached to the appropriate record in the table.  If a point 
#    is to be added in the same location (center of same DEM Raster cell), the sample ID is added to a record (see description 
#    of "DupSample" and "NumDuplicates" fields below) of the point that already exists and the point is not added to the vector.
#
#    Additional fields are added to the copied Vector point database table (ResultSampleTBL):
#
#      "TABLE" - This is the current name of the table that is to be copied from the "Input Vector", and the name of the table 
#        once it is copied to the Result_Vector.
#
#      "ModEasting" - This is the Easting map coordinate of the sample point after it has been moved to the center of the DEM 
#        Raster cell of the nearest flow path.
#
#      "ModNorthing" - This is the Northing map coordinate of the sample point after it has been moved to the center of the DEM 
#        Raster cell of the nearest flow path.
#
#      "Displaced" - This is the distance in meters in which the point was moved from it's original location to the new location.
# 
#      "SnappedToNode" - This is the node element number that the point was moved to if it was in the maximum search distance 
#        specified by the user.  This value will only be set if the "Snap to downstream node" toggle has been enabled.  The value 
#        of (-1) is the default value for invalid.
#
#      "Horton" - The Horton stream order value of the flowpath line that the sample point was moved to.
#
#      "Shreve" - The Shreve stream order value of the flowpath line that the sample point was moved to.
#
#      "Strahler" - The Strahler stream order value of the flowpath line that the sample point was moved to.
#
#      "Scheidegger" - The Scheidegger stream order value of the flowpath line that the sample point was moved to.
#
#      "TotalUpSamples" - This is the total number of samples upstream of the current sample point based on the flow path created.
#
#      "TotalUpChanLen" - This is the sum of all flow path line segments that are considered upstream of the sample point.  This 
#        also includes the length from the sample point to the nearest upstream node.  This value is recorded in kilometers (km).
#
#      "TotalArea_sq_km" - This is the area of the total upstream catch basin for the sample ID attached to the record.
#
#    The following new table (ResultPointToPointTBL) and fields are created in the Vector point database:
#  
#      "PointToPoint" - This is the current name of the point database table that is created to show relationships between the 
#        sample points in the vector.
#
#      "OnLine" - This is the line element number in the STDFLOWPATH Vector that the sample point has been moved to.  If a sample 
#        point was moved to the location of a node, the element number of the line downstream of that node is used.
#
#      "NumDuplicates" - This specifies the number of sample points that were to be moved to the same location.  Because the 
#        duplicate points were not added to the vector, they are listed in the "DupSample#" fields.
#
#      "DupSample#" - This specifies the sample ID of the duplicate point.  The '#' in the field name represents incrementing 
#        numbers (1 to the maximum number of duplicates found for a single point), so each duplicate sample ID for a particular 
#        point has its own field.  These fields are added by the script as necessary.
#
#      "DownSample" - This is the sample ID of the next sample point downstream.  A value of (-1) represents no samples 
#        downstream based on the flow path created.
#
#      "TotalUpSamples" - This is the total number of samples upstream of the current sample point based on the flow path created.
#
#      "NumUpSamples" - This is the number of samples that are upstream of the current sample point based on the flowpath 
#        created.  This only identifies the number that are immediately reachable by tracing up the flow path without tracing 
#        through an additional sample point.
#
#      "UpSample#" - This is the sample ID of an upstream sample point that is immediately reachable by tracing up the flow path 
#        without tracing through any additional sample points.  The '#' in the field name represents incrementing numbers (1 to the 
#        maximum number of upstream sample points for any point), so each upstream sample point has it's own field for that point.
#
#  STDBASIN (Optional) - This is a vector object that is created by the standard watershed process.  The basins computed in this vector 
#    are based on seed points generated by the standard process.  This is copied to the output project file by the script, but 
#    otherwise not used in the additional script computations.
#
#  STDFLOWPATH - This is a vector object that is created by the standard watershed process.  The flow paths computed in this 
#    vector are based on seed points generated by the standard process.
#
#    The following new table (STDFlowPathTBL) and fields are created in the Vector line database:
# 
#      "LineToLine" - This is the current name of the Vector line database table that is created to show up and down stream 
#        relationships between the line segments in the vector.
#
#      "DownNode" - This is the node element number that is considered the downstream endpoint of the line segment.
#
#      "DownLine" - This is the element number of the line that is attached at the "DownNode" and is considered downstream of the 
#        current line segment.
#
#      "UpNode" - This is the element number of the node that is considered the upstream endpoint of the line segment.
#
#      "NumUpLines" - This number represents the number of lines attached to the "UpNode" of the line segment.  These lines are 
#        all considered to be upstream of the current line.  The current line is excluded in this number of lines attached to the node.
#
#      "UpLine#" - This is the element number of the line that is attached to the "UpNode" and is considered upstream of the 
#        current line.  The '#' in the field name represents incrementing numbers from 1 to 7 (the maximum number of possible 
#        upstream lines attached at the same node), so each upstream line number has it's own field.
#
#      "DownSample" - This is the sample ID of the sample point that is located furthest downstream on the line.
# 
#      "UpSample" - This is the sample ID of the sample point that is located furthest upstream on the line.
#
#      "UpChanLen" - This is the sum of lengths of all line segments that are considered upstream of the current line.  This 
#        number is computed in kilometers (km).  The length of the current line is excluded from this number.
# 
#  STDRIDGE (Optional) - This is a vector object that is created by the standard watershed process.  The ridge lines computed in this 
#    vector are based on the standard process.  This is copied to the output project file by the script but otherwise not used 
#    in the additional script computations.
#
#  USDBASIN - This is a vector object that is created by the watershed process.  The basins are generated using the points 
#    that were modified and added to the Result_Vector as seed points for the watershed process.
#
#    Additional fields are added to the copied Vector polygon sediment database table (UBasinSampleTBL):
#
#      "TotalArea_sq_km" - This is the area of the total upstream catch basin for the sample ID attached to the record.  This 
#        includes all upstream basins and the basin for the sample point related by the sample ID.
#
#    The following new table (UBasinPolyToPolyTBL) and fields are created in the Vector polygon database:
#
#      "PolyToPoly" - This is the current name of the polygon database table that is created to show relationships between the 
#        user defined watershed basins (polygons) in the vector.
# 
#      "SAMP_NO" - This is the sample ID of the sample point that was used to create the polygon.  This field name should remain 
#        The same as in the original database table that was copied to the Result_Vector.
#
#      "NumPolyPerSamp" - This is the number of Vector polygons that represent the basin of a single sample ID.  These polygons 
#        are all attached to this record.
#
#      "SampleBasinArea" - This is the sum of the areas for all polygons that represent the basin of a single sample ID.  This 
#        does not include basins that are created upstream due to additional sample points.
#
#      "TotalSampIncl" - This is the number of sample points that are included in the total upstream catch basin for the sample 
#        point.  This includes the sample point attached to this record.
#
#      "TotalArea_sq_km" - This is the area of the total upstream catch basin for the sample ID attached to the record.  This 
#        includes all upstream basins and the basin for the sample point related by the sample ID.
#
#      "TotalUpBasins" - This is the number of basins that make up the total catch basin for the sample point related by the sample ID.
#
#      "NumUpBasins" - This is the number of basins that are upstream basins that are adjacent to the current basin.  This is 
#        the same value as "NumUpSamples" in the "PointToPoint" table in the Result_Vector.
#
#
#  USDFLOWPATH (Optional) - This is a vector object that is created by the watershed process.  The flow paths are generated using the 
#    points that were modified and added to the Result_Vector as seed points for the watershed process.  This is copied to the 
#    output project file by the script but otherwise not used in the additional script computations.
#
#
#  USD_UPFLOWPATH (Optional) - This is the STDFLOWPATH vector that has been clipped using the USDBASIN vector.  Therefore it is considered
#    an upstream flowpath vector of all the user defined sample points.
#
######################################################################################################
######################################################################################################
# Global Variable Declarations
######################################################################################################
numeric DEBUG = 1;
class GUI_DLG mainDLG;
class STATUSDIALOG statusD;
class STATUSCONTEXT statusC;
numeric errXML, errDLG;
string xmlCODE$;
string InputDEMfilename = "", InputVECTORfilename = "", InputMaskfilename;
string InputDEMobjectName, InputVECTORobjectName, InputMaskobjectName;
class STRING OutputProjectFile = _context.ScriptDir;
string OutputSummaryFile = _context.ScriptDir;
class FILE summaryFile;
string message;
class WATERSHED watershed;
class RASTER SampleDEM;
class VECTOR SampleVECT;
class VECTOR WarpVECT;
class VECTOR ResultVECT;
class VECTOR UBasinVECT;
class TRANS2D_MAPGEN warptransparm;
##########################################################
# Define the table's names, descriptions, and fields here:
##########################################################
#
# Names must be kept to 15 charactors or less.  Numbers 
# added to the field name will count against this limit.
#
##########################################################
######################
# Result_Vector Tables
######################
# ResultSampleTBL = "TABLE"
string sampleTblName = "TABLE";
string sampleIDFldName = "SAMP_NO";						# Sample point unique ID
string modEastFldName = "ModEasting";						# Modified Easting value of the moved point
string modNorthFldName = "ModNorthing";						# Modified Northing value of the moved point
string displacedFldName = "Displaced";							# Distance the point was moved from its original location
string hortonFldName = "Horton";								# "Horton" Stream order
string shreveFldName = "Shreve";								# "Shreve" Stream order
string strahlerFldName = "Strahler";								# "Stahler" Stream order
string scheideggerFldName = "Scheidegger";					# "Scheidegger" Stream order
string snapToDownNodeFldName = "SnappedToNode";			# Node the sample was snapped to
string totalUpChanLenFldName = "TotalUpChanLen";         		# Length of the upstream channel
# ResultPointToPointTBL => Table created to show the relationships between the sample points
string PointToPointTblName = "PointToPoint";
string PointToPointTblDesc = "Point to Point Relationships";
string onLineFldName = "OnLine";								# Line number that the sample point is on
string numDuplFldName = "NumDuplicates";					# Number of duplicate samples in the same DEM cell
string dupSampleIDFldName = "DupSample";# + Number		# Duplicate sample point ID, added by script if necessary
string downSampleFldName = "DownSample";					# First downstream sample encountered
string totalUpSamplesFldName = "TotalUpSamples"; 			# Total number of samples upstream
string numUpSamplesFldName = "NumUpSamples"; 			# Number of immediate upstream samples
string upSampleFldName = "UpSample";# + Number				# Immediate upstream sample
######################
# STDFLOWPATH Table
######################
# STDFlowPathTBL => Table created to show the relationships between the lines in the flowpath vector that is created
string LineToLineTblName = "LineToLine";
string LineToLineTblDesc = "Line to Line Flowpath Relationships";
string downNodeFldName = "DownNode";						# The end node of the line that is downstream
string downLineFldName = "DownLine";							# The connected line that is downstream
string upNodeFldName = "UpNode";								# The end node of the line that is upstream
string numUpLinesFldName = "NumUpLines";					# The number of connected lines that are uptream of the UpNode
string upLineFldName = "UpLine";# + Number					# A line connected to the upnode that is upstream
string mostDownSampleFldName = "DownSample";				# The most downstream sample on the line segment
string mostUpSampleFldName = "UpSample";					# The most upstream sample on the line segment
string totalUpChanLengthFldName = "UpChanLen";				# The total length of all lines upstream of the line segment (self excluded)
######################
#USDBASIN Table
######################
# UBasinPolyToPoly => Table created to show the relationships between the user-defined basins
string PolyToPolyTblName = "PolyToPoly";
string PolyToPolyTblDesc = "Polygon to Polygon Basin Relationships";
#string sampleIDFldName = "SAMP_NO";						# Sample ID realated to the basin polygon.  SAME AS ResultSampleTBL
string numPolysPerSampleFldName = "NumPolyPerSamp";		# Number of vector polygons related to that sample
string areaSamplePolysFldName = "SampBasinArea";			# Total of all vector polygons related to that sample
string totalSamplesIncludedFldName = "TotalSampIncl";			# Number of Samples included (self included)
string totalSampleBasinAreaFldName = "TotalArea_sq_km";		# Total area of all polygons upstream of sample point (self included)
string totalSampleBasinsFldName = "TotalUpBasins";			# Total number of sample basins for sample point (self included)
string numUpBasinsFldName = "NumUpBasins";				# Number of basins upstream (self excluded)
######################################################################################################
######################################################################################################
# Dialog XML Code
######################################################################################################
xmlCODE$ = '<?xml version="1.0"?>
<root>
	<dialog id = "MAIN_DIALOG" title="Multi-Sample Watershed Process and Analysis" Buttons = "">
		<groupbox Name = "Select Input Data" ExtraBorder = "4">
			<pane Orientation = "Horizontal">
				<pane Orientation = "Vertical">
					<pushbutton id = "id_INPUT_DEM_BUTTON" Name = "Input DEM... " VertResize = "Fixed" OnPressed = "OnGetInputDEMPressed();" Enabled="true"/>
					<pushbutton id = "id_INPUT_VECTOR_BUTTON" Name = "Input Vector..." VertResize = "Fixed" OnPressed = "OnGetInputVECTORPressed();" Enabled="false"/>
				</pane>
				<pane Orientation = "Vertical">
					<edittext id = "id_INPUT_DEM" Width = "70" ReadOnly = "true" Justify = "left"/>
					<groupbox ExtraBorder = "2">
						<edittext id = "id_INPUT_VECTOR" Width = "70" ReadOnly = "true" Justify = "left"/>
						<pane Orientation = "Horizontal">
							<label Width = "12" TextAlign = "right">Sample Table Name</label>
							<edittext id = "id_TABLE_NAME" HorizResize = "Fixed" Width = "20" ReadOnly = "true"/>
							<label Width = "12" TextAlign = "right">Unique ID Field</label>
							<edittext id = "id_FIELD_NAME" HorizResize = "Fixed" Width = "20" ReadOnly = "true"/>
						</pane>
					</groupbox>
				</pane>
			</pane>
		</groupbox>
		<label/>
		<groupbox Name = "Watershed Options">
			<book>
				<page Name = "General">
					<pane Orientation = "Horizontal">
						<pane Orientation = "Vertical" VertResize = "Fixed">
							<togglebutton id = "id_FILL_ALL_DEP" Name = "Fill All Depressions" Selected = "true" Enabled = "true"/>
							<togglebutton Name = "Compute Databases" Selected = "false" Enabled = "false"/>
						</pane>
						<pane Orientation = "Vertical" VertResize = "Fixed">
							<togglebutton Name = "Compute Standard Flowpath" Selected = "true" Enabled = "false"/>
							<togglebutton id = "id_COMP_STD_BASINS" Name = "Compute Standard Basins" Selected = "false" Enabled = "true"/>
							<togglebutton id = "id_COMP_STD_RIDGES" Name = "Compute Standard Ridges" Selected = "false" Enabled = "true"/>
						</pane>
						<pane Orientation = "Vertical" VertResize = "Fixed">
							<togglebutton Name = "Compute User-Defined Basins" Selected = "true" Enabled = "false"/>
							<togglebutton id = "id_COMP_USR_FLOWPATH" Name = "Compute User-Defined Flowpath" Selected = "false" Enabled = "true"/>
							<togglebutton id = "id_COMP_USR_UP_FLOWPATH" Name = "Compute User-Defined Upstream Flowpath" Selected = "true" Enabled = "true"/>
						</pane>
					</pane>
				</page>
				<page Name = "Mask">
					<togglebutton Name = "Use Watershed Mask" id = "id_MASK_TOGGLE" Enabled = "true" Selected = "false" OnChanged = "OnUseMaskToggleChanged();"/>
					<groupbox ExtraBorder = "2" VertResize = "Fixed">
						<pane Orientation = "Horizontal" VertResize = "Fixed">
							<pane Orientation = "Vertical" HorizResize = "Fixed">
								<pushbutton id = "id_MASK_BUTTON" Name = "Mask..." HorizResize = "Fixed" Enabled = "false" OnPressed = "OnMaskButtonPressed();"/>
							</pane>
							<pane Orientation = "Vertical" HorizResize = "Expand">
								<edittext id = "id_INPUT_MASK" HorizResize = "Expand" Enabled = "false" ReadOnly = "true"/>
							</pane>
						</pane>
					</groupbox>
				</page>
				<page Name = "Flow Path and Basin">
					<pane HorizResize = "Fixed">
						<pane Orientation = "Horizontal">
							<label HorizResize = "Fixed">Threshold</label>
							<pushbutton Name = "Restore Defaults" HorizResize = "Fixed" OnPressed = "OnRestoreDefaultsPressed();"/>
							<togglebutton id = "id_SEP_VALLEY_POLYS" Name = "Separate Valley Polygons"/>
						</pane>
						<groupbox ExtraBorder = "2">
							<pane Orientation = "Horizontal">
								<pane Orientation = "Vertical" HorizResize = "Fixed">
									<label>Outlet</label>
									<label>Branch</label>
								</pane>
								<pane Orientation = "Vertical" HorizResize = "Expand">
									<editnumber id = "id_OUTLET" Default = "256" Precision = "0"/>
									<editnumber id = "id_BRANCH" Default = "128" Precision = "0"/>
								</pane>
								<pane Orientation = "Vertical" HorizResize = "Fixed">
									<label>Inlet</label>
									<label>Basin</label>
								</pane>
								<pane Orientation = "Vertical" HorizResize = "Expand">
									<editnumber id = "id_INLET" Default = "32" Precision = "0"/>
									<editnumber id = "id_BASIN" Default = "378" Precision = "0"/>
								</pane>
							</pane>
						</groupbox>
					</pane>
				</page>
			</book>
		</groupbox>
		<label/>
		<groupbox Name = "Sample Location Adjustment Options" ExtraBorder = "4">
			<pane Orientation = "Horizontal">
				<groupbox ExtraBorder = "2">
					<pane Orientation = "Vertical">
						<togglebutton id = "id_SNAP_LINE_TOGGLE" Name = "Snap to closest flowpath" Selected = "true" Enabled = "false" OnChanged = "OnSnapLineToggleChanged();"/>
						<pane Orientation = "Horizontal">
							<label id = "id_SNAP_LINE_LABEL" TextAlign = "Right">Maximum search distance (meters)</label>
							<editnumber id = "id_SNAP_LINE_MAX_DIST" Width = "10" Precision = "2" Default = "200"/>
						</pane>
					</pane>
				</groupbox>
				<groupbox ExtraBorder = "2">
					<pane Orientation = "Vertical">
						<togglebutton id = "id_SNAP_NODE_TOGGLE" Name = "Snap to downstream node" Selected = "false" OnChanged = "OnSnapNodeToggleChanged();"/>
						<pane Orientation = "Horizontal">
							<label id = "id_SNAP_NODE_LABEL" TextAlign = "Right" Enabled = "false">Maximum search distance (meters)</label>
							<editnumber id = "id_SNAP_NODE_MAX_DIST" Width = "10" Precision = "2" Enabled = "false" Default = "90"/>
						</pane>
					</pane>
				</groupbox>
			</pane>
		</groupbox>
		<label/>
		<groupbox Name = "Select Output Locations" ExtraBorder = "4">
			<pane Orientation = "Horizontal">
				<pane Orientation = "Vertical">
					<pushbutton id = "id_OUTPUT_PROJECT_FILE_BUTTON" Name = "Output Project File..." OnPressed = "OnGetOutputProjectFilePressed();" Enabled="false"/>
					<pushbutton id = "id_OUTPUT_SUMMARY_FILE_BUTTON" Name = "Output Summary File..." OnPressed = "OnGetOutputSummaryFilePressed();" Enabled="false"/>
				</pane>
				<pane Orientation = "Vertical">
					<edittext id = "id_OUTPUT_PROJECT_FILE" Width = "70" ReadOnly = "true" Justify = "left"/>
					<edittext id = "id_OUTPUT_SUMMARY_FILE" Width = "70" ReadOnly = "true" Justify = "left"/>
				</pane>
			</pane>
		</groupbox>
		<pane Orientation = "Horizontal" HorizResize = "Expand">
			<pane Orientation = "Vertical"/>
			<pushbutton id = "id_RUN_BUTTON" Name = "Run..." HorizResize = "Fixed" Enabled = "false" OnPressed = "OnRunPressed();"/>
			<pushbutton id = "id_CANCEL_BUTTON" Name = "Cancel" HorizResize = "Fixed" Enabled = "1" OnPressed = "OnCancelPressed();"/>
		</pane>
	</dialog>
</root>';
######################################################################################################
######################################################################################################
######################################################################################################
# Start of the Script
######################################################################################################
clear();
proc OnCreateDialog(); #Function Prototype
OnCreateDialog();
######################################################################################################
######################################################################################################
func string SampleIDThatIsNotUnique ( ) {
	##################################################################################################
	# Checking to make sure that all the sample IDs in the point vector table are unique
	statusC.Message = "Verifying all sample IDs are unique";
	##################################################################################################
	local class VECTOR UniqueCheckVector;
	OpenVector(UniqueCheckVector, InputVECTORfilename, InputVECTORobjectName);
	local class DATABASE pntDBase = OpenVectorPointDatabase(UniqueCheckVector);
	local class DBTABLEINFO tblInfo = DatabaseGetTableInfo(pntDBase, sampleTblName);
	local numeric numTotalRecords = tblInfo.NumRecords;
	numeric uniquehash[];
	local numeric index;
	local string key;
	for index = 1 to numTotalRecords {
		key = TableReadFieldStr(tblInfo, sampleIDFldName, index);
		if ( uniquehash.Exists(key) ) {
			uniquehash.Clear();
			print(index, key, sampleIDFldName);
			return key;
		} else {
			uniquehash[key] = 1;
		}
	}
	uniquehash.Clear();
	return "";
}
######################################################################################################
######################################################################################################
proc StartWatershedProcess ( ) {
	##################################################################################################
	# Initialize the watershed objects and create the output objects for the script
	statusC.Message = "Step 1 of 18:  Computing standard Watershed objects";
	##################################################################################################
	watershed = WatershedInit(InputDEMfilename, InputDEMobjectName);
	WatershedSetBasin(watershed, mainDLG.GetCtrlByID("id_BASIN").GetValueNum());
	WatershedSetInlet(watershed, mainDLG.GetCtrlByID("id_INLET").GetValueNum());
	WatershedSetBranch(watershed, mainDLG.GetCtrlByID("id_BRANCH").GetValueNum());
	WatershedSetOutlet(watershed, mainDLG.GetCtrlByID("id_OUTLET").GetValueNum());
	if (mainDLG.GetCtrlByID("id_SEP_VALLEY_POLYS").GetValueNum()) {
		WatershedSetValleySeparation(watershed, mainDLG.GetCtrlByID("id_BASIN").GetValueNum());
	}
	if (mainDLG.GetCtrlByID("id_MASK_TOGGLE").GetValueNum()) {
		if (ObjectExists(InputMaskfilename, InputMaskobjectName, "Raster")) {
			WatershedSetMask(watershed, InputMaskfilename, InputMaskobjectName);
		} else {
			message = "##########################################################################################\n" + 
			         "Warning:  The Mask Raster selected for the watershed process is invalid.\n" +
			         "         A full watershed analysis will be done instead.\n" + 
			         "##########################################################################################\n\n";
			print(message);
			fwritestring(summaryFile, message);
		}
	}
	# Set up the watershed object creation parameters
	local string watershedparms$ = "";
	if ( mainDLG.GetCtrlByID("id_FILL_ALL_DEP").GetValueNum()) {
		watershedparms$ += "FillAllDepressions,";
	}
	if ( mainDLG.GetCtrlByID("id_COMP_STD_BASINS").GetValueNum()) {
		watershedparms$ += "Basin,";
	}
	if ( mainDLG.GetCtrlByID("id_COMP_STD_RIDGES").GetValueNum()) {
		watershedparms$ += "Ridge,";
	}
#	if ( mainDLG.GetCtrlByID("id_COMP_STD_DBASE").GetValueNum()) {
#		watershedparms$ += "Database,";
#	}
	watershedparms$ += "FlowPath";
	print(watershedparms$);
	WatershedCompute(watershed, watershedparms$);
	#array numeric xvals[0];
	#array numeric yvals[0];
	#WatershedComputeElements(watershed, xvals, yvals, 0, "FlowPath");
	# Copy Standard Watershed Objects
	local string ObjectFilename, ObjectName;
	local numeric ObjNumber; 
	WatershedGetObject(watershed, "VectorFlowPath", ObjectFilename, ObjectName);
	ObjNumber = ObjectNumber(ObjectFilename, ObjectName, "VECTOR");
	CopyObject(ObjectFilename, ObjNumber, OutputProjectFile);
	if ( mainDLG.GetCtrlByID("id_COMP_STD_BASINS").GetValueNum()) {
		WatershedGetObject(watershed, "VectorBasin", ObjectFilename, ObjectName);
		ObjNumber = ObjectNumber(ObjectFilename, ObjectName, "VECTOR");
		CopyObject(ObjectFilename, ObjNumber, OutputProjectFile);
	}
	if ( mainDLG.GetCtrlByID("id_COMP_STD_RIDGES").GetValueNum()) {
		WatershedGetObject(watershed, "VectorRidge", ObjectFilename, ObjectName);
		ObjNumber = ObjectNumber(ObjectFilename, ObjectName, "VECTOR");
		CopyObject(ObjectFilename, ObjNumber, OutputProjectFile);
	}
	# Create new vector to contain the result of the script
	OpenRaster(SampleDEM, InputDEMfilename, InputDEMobjectName);
	OpenVector(SampleVECT, InputVECTORfilename, InputVECTORobjectName);
	local class GEOREF DEMgeoref = GetLastUsedGeorefObject(SampleDEM);
	local class GEOREF VECTgeoref = GetLastUsedGeorefObject(SampleVECT);
	warptransparm.InputCoordRefSys = VECTgeoref.CoordRefSys;
	warptransparm.OutputCoordRefSys = DEMgeoref.CoordRefSys;
	CreateVector(WarpVECT, OutputProjectFile, "WarpVect", "Warped Input Vector");   #XXX
	VectorWarp(SampleVECT, WarpVECT, warptransparm, 10);
	# Compute the extents of the WarpVECTOR to use in initializing the results vector.
#	VectorValidate(WarpVECT);
	local class REGION2D extents = GetObjectExtentsRegion(WarpVECT, GetLastUsedGeorefObject(WarpVECT));
	CreateVector(ResultVECT, OutputProjectFile, "Result Vector", "Resulting Vector created by SML Watershed Script", "VectorToolkit", extents.Extents);
#	VectorCopyElements(WarpVECT, ResultVECT, "RemoveTables", 0, 0, 0);
	CopySubobjects(WarpVECT, ResultVECT, "GEOREF");
	local class GEOREF WARPgeoref = GetLastUsedGeorefObject(WarpVECT);
	# Open the standard flowpath vector
	local class VECTOR STDFlowPath;
	OpenVector(STDFlowPath, OutputProjectFile, "STDFLOWPATH");
	##################################################################################################
	# Setup the Database tables used during the script
	statusC.Message =  "Step 2 of 18:  Creating new database tables";
	##################################################################################################
	# Copy the sample database table from original vector
	local class DATABASE SampleDB = OpenVectorPointDatabase(WarpVECT);
	local class DBTABLEINFO SampleTBL = DatabaseGetTableInfo(SampleDB, sampleTblName);
	local class DATABASE ResultDB = OpenVectorPointDatabase(ResultVECT);
	if (TableCopy(SampleDB, SampleTBL, ResultDB) < 0) {
		message = "Error occured while trying to copy the sample table.\n" + "Exiting the script now... \n";
		print(message);
		fwritestring(summaryFile, message);
		CloseRaster(SampleDEM);
		CloseVector(SampleVECT);
		CloseVector(WarpVECT);
		CloseVector(ResultVECT);
		Exit();
	}
	# Add additional fields to the sample table to note movement in the sample locations
	local class DBTABLEINFO ResultSampleTBL = DatabaseGetTableInfo(ResultDB, sampleTblName);
	TableAddFieldFloat(ResultSampleTBL, modEastFldName, 15, 4);						# Modified Easting position of the sample point
	TableAddFieldFloat(ResultSampleTBL, modNorthFldName, 15, 4);					# Modified Northing position of the sample point
	TableAddFieldFloat(ResultSampleTBL, displacedFldName, 10, 3);					# Displacement in meters from the original to modified position
	TableAddFieldInteger(ResultSampleTBL, snapToDownNodeFldName, 10);				# Specifies the downstream node that it was snapped to if the node
																										#   was within the distance and on the closests line
	TableAddFieldInteger(ResultSampleTBL, hortonFldName, 7);							# "Horton" stream order of the sample
	TableAddFieldInteger(ResultSampleTBL, shreveFldName, 7);							# "Shreve" stream order of the sample
	TableAddFieldInteger(ResultSampleTBL, strahlerFldName, 9);						# "Strahler" stream order of the sample
	TableAddFieldInteger(ResultSampleTBL, scheideggerFldName, 12);					# "Scheidegger" stream order of the sample
	TableAddFieldInteger(ResultSampleTBL, totalUpSamplesFldName, 10);				# Total number of upstream sample points
	TableAddFieldFloat(ResultSampleTBL, totalUpChanLenFldName, 10, 3);			# The total upstream channel length being sampled in kilometers
	TableAddFieldFloat(ResultSampleTBL, totalSampleBasinAreaFldName, 15, 4); 	# The total area of the upstream catchment basin
	# Create a table to show relationship between the sample points
	local class DBTABLEINFO ResultPointToPointTBL;
	ResultPointToPointTBL = TableCreate(ResultDB, PointToPointTblName, PointToPointTblDesc);
	TableAddFieldInteger(ResultPointToPointTBL, onLineFldName, 10);				# Line number that the sample point is on
	TableAddFieldInteger(ResultPointToPointTBL, numDuplFldName, 13);				# Number of duplicate samples in the same DEM cell
	#TableAddFieldString(ResultPointToPointTBL, dupSampleIDFldName + #, 12); 	# Duplicate sample point ID, added by script if necessary
	TableAddFieldString(ResultPointToPointTBL, downSampleFldName, 11);			# First downstream sample encountered
	TableAddFieldInteger(ResultPointToPointTBL, totalUpSamplesFldName, 10); 	# Total number of samples upstream
	TableAddFieldInteger(ResultPointToPointTBL, numUpSamplesFldName, 10); 		# Number of immediate upstream samples
	TableAddFieldString(ResultPointToPointTBL, upSampleFldName + "1", 11);		# Immediate upstream sample
	TableAddFieldString(ResultPointToPointTBL, upSampleFldName + "2", 11);		# Additional immediate upstream sample
	#TableAddFieldString(ResultPointToPointTBL, upSampleFldName + "#", 11);		# Additional added by script if necessary
	# Create a table to show the relationships between the flowpath lines created by the watershed 
	# process to help determine the direction of the flowpath.  Also shows the most upstream and 
	# downstream sample points on each line.
	local class DATABASE STDFlowPathDB = OpenVectorLineDatabase(STDFlowPath);
	local class DBTABLEINFO STDFlowPathTBL;
	STDFlowPathTBL = TableCreate(STDFlowPathDB, LineToLineTblName, LineToLineTblDesc);
	TableAddFieldInteger(STDFlowPathTBL, downNodeFldName, 10);					# The end node of the line that is downstream
	TableAddFieldInteger(STDFlowPathTBL, downLineFldName, 10);					# The connected line that is downstream
	TableAddFieldInteger(STDFlowPathTBL, upNodeFldName, 10);						# The end node of the line that is upstream
	TableAddFieldInteger(STDFlowPathTBL, numUpLinesFldName, 10);				# The number of connected lines that are uptream of the UpNode
	TableAddFieldInteger(STDFlowPathTBL, upLineFldName + "1", 10);				# A line connected to the upnode that is upstream
	TableAddFieldInteger(STDFlowPathTBL, upLineFldName + "2", 10);				# A line connected to the upnode that is upstream
	TableAddFieldInteger(STDFlowPathTBL, upLineFldName + "3", 10);				# A line connected to the upnode that is upstream
	TableAddFieldInteger(STDFlowPathTBL, upLineFldName + "4", 10);				# A line connected to the upnode that is upstream
	TableAddFieldInteger(STDFlowPathTBL, upLineFldName + "5", 10);				# A line connected to the upnode that is upstream
	TableAddFieldInteger(STDFlowPathTBL, upLineFldName + "6", 10);				# A line connected to the upnode that is upstream
	TableAddFieldInteger(STDFlowPathTBL, upLineFldName + "7", 10);				# A line connected to the upnode that is upstream
	TableAddFieldString(STDFlowPathTBL, mostDownSampleFldName, 11);			# The most downstream sample on the line segment
	TableAddFieldString(STDFlowPathTBL, mostUpSampleFldName, 11);				# The most upstream sample on the line segment
	TableAddFieldFloat(STDFlowPathTBL, totalUpChanLengthFldName, 15, 3);		# The total length of all lines upstream of the line segment (self excluded)
	##################################################################################################
	# Common variables used in multiple steps
	##################################################################################################
	local numeric numOriginalSamplePoints;
	local numeric numSamplePoints;
	local numeric i, j, k, node1, node2;
	local class POLYLINE P;
	local class POINT2D pt1, pt2;
	local array numeric records[100];
	local numeric Temp1MapX, Temp1MapY, Temp1ObjX, Temp1ObjY, Temp2MapX, Temp2MapY;
	local numeric numFlowPathLines = NumVectorLines(STDFlowPath);
	local string tempFieldName;
	local string sampleNum$;
	local array numeric elements[10];
	local class STRINGLIST UpSample;
	local string tempStr$;
	local string tempID1, tempID2;
	local numeric warnCountDuplicates = 0;
	local numeric warnCountOutsideSearch = 0;
	local numeric warnCountLostPolygons = 0;
	local numeric warnCountManAttachPolygons = 0;
	local numeric numComputed;
	local array numeric lines1[10], lines2[10];
	# Get the georeference objects for transformations
	local class GEOREF FLOWgeoref = GetLastUsedGeorefObject(STDFlowPath);
	local class GEOREF RSLTgeoref = GetLastUsedGeorefObject(ResultVECT);
	##################################################################################################
	# Determine the upstream and downstream relationship based on the STDFlowPath Vector
	statusC.Message = "Step 3 of 18:  Computing up and downstream flowpath relationships";
	##################################################################################################
	# Variables used
	local numeric numAttachedLines1, numAttachedLines2, currentShreveValue;
	local array numeric upLines[7];
	local numeric downLine, upNode, downNode, lineCount, numUpLines;
	# For each line in the STDFlowPath Vector
	statusC.BarInit(numFlowPathLines, 0);
	for i = 1 to numFlowPathLines {
		statusC.BarIncrement(1, 0);
		# Initialize the variables to invalid
		downNode = -1;
		downLine = -1;
		upNode = -1;
		for j = 1 to 7 {
			upLines[j] = -1;
		}
		currentShreveValue = STDFlowPath.line[i].STREAM_ORDER.Shreve;
		# Get the line
		P = GetVectorLine(STDFlowPath, i);
		# Get the first end point and the lines attached to it
		pt1 = P.GetVertex(0);
		pt1 = ObjectToMap(STDFlowPath, pt1.x, pt1.y, FLOWgeoref);
		node1 = FindClosestNode(STDFlowPath, pt1.x, pt1.y, FLOWgeoref);
		numAttachedLines1 = GetVectorNodeLineList(STDFlowPath, lines1, node1);
		# Get the second end point and the lines attached to it
		pt2 = P.GetVertex(P.GetNumPoints() - 1);
		pt2 = ObjectToMap(STDFlowPath, pt2.x, pt2.y, FLOWgeoref);
		node2 = FindClosestNode(STDFlowPath, pt2.x, pt2.y, FLOWgeoref);
		numAttachedLines2 = GetVectorNodeLineList(STDFlowPath, lines2, node2);
		# If the only lines attached to both nodes is the line connecting the two nodes
		if ( (numAttachedLines1 == 1) and (numAttachedLines2 == 1) ) {
			numUpLines = 0;
			# Determine upstream based on Z values of the nodes.  Tie sets upstream to node1
			if (STDFlowPath.Node[node1].Internal.z >= STDFlowPath.Node[node2].Internal.z) {
				downNode = node2;
				upNode = node1;
			} else {
				downNode = node1;
				upNode = node2;
			}
		# If node1 has multiple attached lines and node2 has 1 
		} else if ( (numAttachedLines1 > 1) and (numAttachedLines2 == 1) ) { 
			# For each line attached to node1
			for j = 1 to numAttachedLines1 {
				# Don't check the current line
				if (lines1[j] != i) {
					# Check the Shreve stream order system.  Greatest value will be downstream
					if (STDFlowPath.line[lines1[j]].STREAM_ORDER.Shreve > currentShreveValue) {
						currentShreveValue = STDFlowPath.line[lines1[j]].STREAM_ORDER.Shreve;
						downLine = lines1[j];
						downNode = node1;
						upNode = node2; 
					}
				}
			}
			# If node1 is not downstream, node2 must be
			if (downNode == -1) {
				downNode = node2;
				upNode = node1;
			}
		} else if ( (numAttachedLines1 == 1) and (numAttachedLines2 > 1) ) {
			# For each line attached to node2
			for j = 1 to numAttachedLines2 {
				# Don't check the current line
				if (lines2[j] != i) {
					# Check the Shreve stream order system.  Greatest value will be downstream
					if (STDFlowPath.line[lines2[j]].STREAM_ORDER.Shreve > currentShreveValue) {
						currentShreveValue = STDFlowPath.line[lines2[j]].STREAM_ORDER.Shreve;
						downLine = lines2[j];
						downNode = node2;
						upNode = node1;
					}
				}
			}
			# If node2 is not downstream, node1 must be
			if (downNode == -1) {
				downNode = node1;
				upNode = node2;
			}
		# Both nodes have multiple attached lines
		} else {
			# For each line attached to node1
			for j = 1 to numAttachedLines1 {
				# Don't check the current line
				if (lines1[j] != i) {
					# Check the Shreve stream order system.  Greatest value will be downstream
					if (STDFlowPath.line[lines1[j]].STREAM_ORDER.Shreve > currentShreveValue) {
						currentShreveValue = STDFlowPath.line[lines1[j]].STREAM_ORDER.Shreve;
						downLine = lines1[j];
						downNode = node1;
						upNode = node2;
					}
				}
			}
			# For each line attached to node2
			for j = 1 to numAttachedLines2 {
				# Don't check the current line
				if (lines2[j] != i) {
					# Check the Shreve stream order system.  Greatest value will be downstream
					if (STDFlowPath.line[lines2[j]].STREAM_ORDER.Shreve > currentShreveValue) {
						currentShreveValue = STDFlowPath.line[lines2[j]].STREAM_ORDER.Shreve;
						downLine = lines2[j];
						downNode = node2;
						upNode = node1;
					}
				}
			}
		} # End of finding nodes and attached lines 
		# If node1 is the down node, then lines2 are the upstream lines
		lineCount = 0;
		if (downNode == node1) {
			numUpLines = numAttachedLines2 - 1;
			for k = 1 to numAttachedLines2 {
				if (lines2[k] != i) {
					lineCount = lineCount + 1;
					upLines[lineCount] = lines2[k];
				}
			}
		# If node2 is the down node, then lines1 are the upstream lines
		} else if (downNode == node2) {
			numUpLines = numAttachedLines1 - 1;
			for k = 1 to numAttachedLines1 {
				if (lines1[k] != i) {
					lineCount = lineCount + 1;
					upLines[lineCount] = lines1[k];
				}
			}
		# Print message if unable to determine....  Shouldn't happen though
		} else {
			message = "Warning: Unable to determine upstream and downstream configuration for line: " + NumToStr(i) + "\n\n";
			print(message);
			fwritestring(summaryFile, message);
		}
		# Write record of the line that is downstream
		records[1] = TableWriteRecord(STDFlowPathTBL, 0, downNode, downLine, upNode, numUpLines, 
				upLines[1], upLines[2], upLines[3], upLines[4], upLines[5], upLines[6], upLines[7]);
		TableWriteAttachment(STDFlowPathTBL, i, records, 1, "line");
	}
	##################################################################################################
	# Determine the upstream channel lengths
	statusC.Message = "Step 4 of 18:  Computing upstream channel lengths for each flowpath line";
	##################################################################################################
	local numeric upLine, currUpLength, tempUpLength, tempLength, tempCount = 0;
	numComputed = 0;
	# For each line in the STDFlowPath Vector
	statusC.BarInit(numFlowPathLines, 0);
	for i = 1 to numFlowPathLines {
		statusC.BarIncrement(1, 0);
		TableReadAttachment(STDFlowPathTBL, i, records, "line");
		upLine = TableReadFieldNum(STDFlowPathTBL, upLineFldName + "1", records[1]);
		# If the current line does not have an upstream line, set it's Upstream Channel Length to 0;
		if (upLine == -1) {
			TableWriteField(STDFlowPathTBL, records[1], totalUpChanLengthFldName, 0);
			numComputed = numComputed + 1;
		# Otherwise set it to invalid stating it needs to be computed
		} else {
			TableWriteField(STDFlowPathTBL, records[1], totalUpChanLengthFldName, -1);
		}
	}
	# Until all upstream field that can be computed, are computed
	statusC.BarInit(numFlowPathLines, 0);
	while (numComputed > 0) {
		# Reset the counter and go though all the flowpath lines
		tempCount += numComputed;
		statusC.BarIncrement(numComputed, 0);
		numComputed = 0;
		for i = 1 to numFlowPathLines {
			TableReadAttachment(STDFlowPathTBL, i, records, "line");
			currUpLength = TableReadFieldNum(STDFlowPathTBL, totalUpChanLengthFldName, records[1]);
			# If the Upstream length has not been computed
			if (currUpLength == -1) {
				numUpLines = TableReadFieldNum(STDFlowPathTBL, numUpLinesFldName, records[1]);
				tempUpLength = 0;
				# For each upstream line
				for j = 1 to numUpLines {
					# Check that it hasn't failed yet
					if (tempUpLength != -1) {
						# Get one of the Upstream lines
						TableReadAttachment(STDFlowPathTBL, i, records, "line");
						upLine = TableReadFieldNum(STDFlowPathTBL, upLineFldName + NumToStr(j), records[1]);
						# Get it's upstream channel length value
						TableReadAttachment(STDFlowPathTBL, upLine, records, "line");
						tempLength = TableReadFieldNum(STDFlowPathTBL, totalUpChanLengthFldName, records[1]);
						# If it has been computed, add the value and the length of this upstream line to the temp total
						if (tempLength != -1) {
							tempUpLength = tempUpLength + tempLength;
							tempUpLength = tempUpLength + (STDFlowPath.line[upLine].LINESTATS.Length / 1000);
						# If it has not been computed, invalidate the total and move on
						} else {
							tempUpLength = -1;
						}
					}
				}
				# If the Upstream Channel length was successfully computed
				if (tempUpLength != -1) {
					numComputed = numComputed + 1;
					TableReadAttachment(STDFlowPathTBL, i, records, "line");
					TableWriteField(STDFlowPathTBL, records[1], totalUpChanLengthFldName, tempUpLength);
				}
			} # End if currUpLength needs to be computed
		} # End for i loop
	} # End while
	##################################################################################################
	# Move the sample points to the closest flowpath line.  If close enough, move to the end node
	# of that line if the displacement distance is within the user specified maximum value.  Once on
	# the line, move to the center of that DEM raster cell.
	statusC.Message = "Step 5 of 18:  Computing best sample point locations based on user supplied seed points";
	##################################################################################################
	# Determine the number of relevant vector points that will be used
	local numeric numOriginalSamplePoints = NumVectorPoints(WarpVECT);
	local numeric currNumDups, currMaxNumDups = 0, numPointsAdded = 0;
	array numeric x[numOriginalSamplePoints], y[numOriginalSamplePoints];
	# Move each point to the closest flowpath and add to result vector
	local numeric snappedToNode, pointExists;
	local numeric maxSnapNodeDist = mainDLG.GetCtrlByID("id_SNAP_NODE_MAX_DIST").GetValueNum();
	local numeric maxSnapLineDist = mainDLG.GetCtrlByID("id_SNAP_LINE_MAX_DIST").GetValueNum();
	local numeric pt1Dist, pt2Dist, tempDist;
	local numeric OrigMapX, OrigMapY, OrigObjX, OrigObjY;
	local numeric MovedMapX, MovedMapY, MovedObjX, MovedObjY; 
	local numeric VectObjX, VectObjY;
	local numeric err, lineNum, numRecords;
	local numeric distance, azimuth, elevation;  #azimuth and elev are not used
	local class DBFIELDINFO beforeField = FieldGetInfoByName(ResultPointToPointTBL, downSampleFldName);
	statusC.BarInit(numOriginalSamplePoints, 0);
	for i = 1 to numOriginalSamplePoints {
		statusC.BarIncrement(1, 0);
		OrigObjX = WarpVECT.Point[i].Internal.x;  # In object coordinates
		OrigObjY = WarpVECT.Point[i].Internal.y;
		ObjectToMap(WarpVECT, OrigObjX, OrigObjY, WARPgeoref, OrigMapX, OrigMapY);
		# Move to the closed point on the closest flowpath line in object coordinates
		lineNum = FindClosestLine(STDFlowPath, OrigMapX, OrigMapY, FLOWgeoref, maxSnapLineDist);
		# Ignore the point if the line that it is attached to does not have a stream order record assosiated with it
		if (TableReadAttachment(STDFlowPathTBL, lineNum, records, "line") == 0) {
			TableReadAttachment(SampleTBL, i, records, "point");
			tempID1 = TableReadFieldStr(SampleTBL, sampleIDFldName, records[1]);
			warnCountOutsideSearch = warnCountOutsideSearch + 1;
			message = "Warning:  Point " + tempID1 + " was to be moved to the standard flowpath line " + NumToStr(lineNum) + ",  but the stream order for\n" +
			          "          for this line was unable to be determined.  Since the script uses the standard\n" +
			          "          flowpath vector to compute up and downstream relationships, this point is being\n" +
			          "          discarded and not used.\n\n";
			print(message);
			fwritestring(summaryFile, message);
			continue;
		} 
		MapToObject(FLOWgeoref, OrigMapX, OrigMapY, STDFlowPath, OrigObjX, OrigObjY);
		ClosestPointOnLine(STDFlowPath, lineNum, OrigObjX, OrigObjY, MovedObjX, MovedObjY);
		ObjectToMap(STDFlowPath, MovedObjX, MovedObjY, FLOWgeoref, MovedMapX, MovedMapY);
		Displacement3D(OrigMapX, OrigMapY, 0, MovedMapX, MovedMapY, 0, tempDist, azimuth, elevation);
		if (tempDist > maxSnapLineDist) {
			TableReadAttachment(SampleTBL, i, records, "point");
			tempID1 = TableReadFieldStr(SampleTBL, sampleIDFldName, records[1]);
			warnCountOutsideSearch = warnCountOutsideSearch + 1;
			message = "Warning:  Point " + tempID1 + " was found to be " + NumToStr(tempDist) + " meters from the closest computed standard\n" +
			          "          flowpath line.  This sample point will not be included in the remaining analysis\n" +
			          "          of this script.  Adjust the Flow Path and Basin parameters in the main dialog\n" +
			          "          or modify the original location of the sample point and run the script again.\n\n";
			print(message);
			fwritestring(summaryFile, message);
		} else {
			# If user specifies, snap to the downstream node of the closest line within specified maximum search distance
			snappedToNode = 0;
			if (mainDLG.GetCtrlByID("id_SNAP_NODE_TOGGLE").GetValueNum()) {
				P = GetVectorLine(STDFlowPath, lineNum);
				# Get the two endpoints of the line and determine the distance to them from the orignal point
				pt1 = P.GetVertex(0);
				ObjectToMap(STDFlowPath, pt1.x, pt1.y, FLOWgeoref, Temp1MapX, Temp1MapY);
				node1 = FindClosestNode(STDFlowPath, Temp1MapX, Temp1MapY, FLOWgeoref);
				Displacement3D(MovedMapX, MovedMapY, 0, Temp1MapX, Temp1MapY, 0, pt1Dist, azimuth, elevation);
				pt2 = P.GetVertex(P.GetNumPoints() - 1);
				ObjectToMap(STDFlowPath, pt2.x, pt2.y, FLOWgeoref, Temp2MapX, Temp2MapY);
				node2 = FindClosestNode(STDFlowPath, Temp2MapX, Temp2MapY, FLOWgeoref);
				Displacement3D(MovedMapX, MovedMapY, 0, Temp2MapX, Temp2MapY, 0, pt2Dist, azimuth, elevation);
				# If the downstream node is node1 and is within the snapping distance
				if ( (pt1Dist < maxSnapNodeDist) and (node1 == STDFlowPath.line[lineNum].(LineToLineTblName).(downNodeFldName)) ) {
					MovedMapX = Temp1MapX;
					MovedMapY = Temp1MapY;
					snappedToNode = node1;
				# If the downstream node is node 2 and is within the snapping distance
				} else if ( (pt2Dist < maxSnapNodeDist) and (node2 == STDFlowPath.line[lineNum].(LineToLineTblName).(downNodeFldName)) ) {
					MovedMapX = Temp2MapX;
					MovedMapY = Temp2MapY;
					snappedToNode = node2;
				}
			}
			# Snap the new points to the center of the DEM raster cells
			MapToObject(DEMgeoref, MovedMapX, MovedMapY, SampleDEM, MovedObjX, MovedObjY);
			MovedObjX = floor(MovedObjX) + 0.5;
			MovedObjY = floor(MovedObjY) + 0.5;
			ObjectToMap(SampleDEM, MovedObjX, MovedObjY, DEMgeoref, MovedMapX, MovedMapY);
			# Convert from map coordinates to vector coordinates
			MapToObject(WARPgeoref, MovedMapX, MovedMapY, WarpVECT, VectObjX, VectObjY);
			# Check to see if the point already exists
			pointExists = 0;
			for j = 1 to numPointsAdded {
				Temp1ObjX = ResultVECT.Point[j].Internal.x;  # In object coordinates
				Temp1ObjY = ResultVECT.Point[j].Internal.y;
				# If both are in the center of the same cell, it's a duplicate
				if ( (VectObjX == Temp1ObjX) and (VectObjY == Temp1ObjY) ) {
					pointExists = j;
					j = numPointsAdded;
				}
			}
			# If the point already exists, mark the current point as a duplicate of the existing point
			# The element number with the lowest value will always be the point added.  Elements
			# with a higher value will always be considered the duplicate since a point already exists.
			if (pointExists > 0) {
				TableReadAttachment(SampleTBL, i, records, "point");
				tempID1 = TableReadFieldStr(SampleTBL, sampleIDFldName, records[1]);
				TableReadAttachment(ResultSampleTBL, pointExists, records, "point");
				tempID2 = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
				warnCountDuplicates = warnCountDuplicates + 1;
				message = "Warning:  Point " + tempID1 + " was found to be a duplicate of point " + tempID2 + " because both points\n" +
				          "          were going to be placed in the center of the same cell of the DEM.\n" +
				          "          The sample number has been recorded as being a duplicate of point " + tempID2 + ".\n\n";
				print(message);
				fwritestring(summaryFile, message);
				TableReadAttachment(ResultPointToPointTBL, pointExists, records, "point");
				currNumDups = TableReadFieldNum(ResultPointToPointTBL, numDuplFldName, records[1]);
				currNumDups = currNumDups + 1;
				# Add additional duplicate sample fields if additional are needed
				while (currNumDups > currMaxNumDups) {
					currMaxNumDups = currMaxNumDups + 1;
					tempFieldName = dupSampleIDFldName + NumToStr(currMaxNumDups);
					# Insert the field before the "OnLine" field
					TableInsertFieldString(ResultPointToPointTBL, tempFieldName, beforeField, 12);
				}
				TableReadAttachment(SampleTBL, i, records, "point");
				sampleNum$ = TableReadFieldStr(SampleTBL, sampleIDFldName, records[1]);
				# Add the sample point to the table as a duplicate
				TableReadAttachment(ResultPointToPointTBL, pointExists, records, "point");
				TableWriteField(ResultPointToPointTBL, records[1], numDuplFldName, currNumDups);
				tempFieldName = dupSampleIDFldName + NumToStr(currNumDups);
				TableWriteField(ResultPointToPointTBL, records[1], tempFieldName, sampleNum$);
			# Add the point if it doesn't exist
			} else {
				numPointsAdded = numPointsAdded + 1;
				x[numPointsAdded] = MovedObjX - 1;
				y[numPointsAdded] = MovedObjY - 1;
				# Check if the point ended up getting moved to a downstream node.  If so, attach it to that downstream line
				node1 = FindClosestNode(STDFlowPath, MovedMapX, MovedMapY, FLOWgeoref);
				pt1.x = STDFlowPath.node[node1].Internal.x;
				pt1.y = STDFlowPath.node[node1].Internal.y;
				pt1 = ObjectToMap(STDFlowPath, pt1.x, pt1.y, FLOWgeoref);
				pt1 = MapToObject(DEMgeoref, pt1.x, pt1.y, SampleDEM);
				pt1.x = floor(pt1.x) + 0.5;
				pt1.y = floor(pt1.y) + 0.5;
				# If the node and the point are in the center of the same raster cell, then consider the point on the downstream
				# line of that node
				if ((pt1.x == MovedObjX) && (pt1.y == MovedObjY)) {
					# If the closest node is the downstream node
					if (node1 == STDFlowPath.line[lineNum].(LineToLineTblName).(downNodeFldName)) {
						# Assign the line the sample is on to the downstream line 
						if (STDFlowPath.line[lineNum].(LineToLineTblName).(downLineFldName) != -1) {
							lineNum = STDFlowPath.line[lineNum].(LineToLineTblName).(downLineFldName);
						}
					}
				}
				# Assumes the vector point added has the element number of the current numPointsAdded
				VectorAddPoint(ResultVECT, VectObjX, VectObjY, SampleDEM[MovedObjX, MovedObjY]);
				numRecords = TableReadAttachment(SampleTBL, i, records, "point");
				err = TableWriteAttachment(ResultSampleTBL, numPointsAdded, records, numRecords, "point");
				if (err <= 0) {
					message = "Warning:  Either an error occured while attaching records to the new point vector,\n" +
					          "          or there are no attached records for element point number: " + NumToStr(i) + ".\n\n";
					print(message);
					fwritestring(summaryFile, message);
				}
				# Calculate the distance the sample point was displaced by
				Displacement3D(OrigMapX, OrigMapY, 0, MovedMapX, MovedMapY, 0, distance, azimuth, elevation);
				# Add the moved points and displacement values to the database
				for j = 1 to numRecords {
					TableWriteField(ResultSampleTBL, records[j], modEastFldName, MovedMapX);
					TableWriteField(ResultSampleTBL, records[j], modNorthFldName, MovedMapY);
					TableWriteField(ResultSampleTBL, records[j], displacedFldName, distance);
					TableWriteField(ResultSampleTBL, records[j], snapToDownNodeFldName, snappedToNode);
				}
				records[1] = TableWriteRecord(ResultPointToPointTBL, 0, lineNum); 
				TableWriteAttachment(ResultPointToPointTBL, numPointsAdded, records, 1, "point");
			}
		}
	}
	VectorValidate(ResultVECT);
	if (NumVectorPoints(ResultVECT) == 0) {
		message = "None of the sample points were found to be within the specified maximum search distance.\n" +
				   "Please verify that your search distance settings are appropriate.\n \n" +
					"Hint:  The standard flowpath vector has already been created and should be in the output project file.\n" +
					"         Use this flowpath vector object along with your point vector to determine appropriate setting values."; 
		PopupMessage(message);
		print(message);
		fwritestring(summaryFile, message);
		CloseVector(ResultVECT);
		CloseRaster(SampleDEM);
		CloseVector(SampleVECT);
		CloseVector(WarpVECT);
		return;
	}
	message = "##########################################################################################\n" +
	          "Number of Unmovable points found: " + NumToStr(warnCountOutsideSearch) + "\n" +
	          "Number of Duplicate points found: " + NumToStr(warnCountDuplicates) + "\n" +
	          "##########################################################################################\n\n";
	print(message);
	fwritestring(summaryFile, message);
	#######################################################################################################
	# Copy the multiple stream order schemas to the point sediment sample table
	statusC.Message = "Step 6 of 18:  Copying stream order schemas to sample databases";
	#######################################################################################################
	numSamplePoints = NumVectorPoints(ResultVECT);
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		TableReadAttachment(ResultPointToPointTBL, i, records, "point");
		lineNum = TableReadFieldNum(ResultPointToPointTBL, onLineFldName, records[1]);
		TableReadAttachment(ResultSampleTBL, i, records, "point");
		TableWriteField(ResultSampleTBL, records[1], hortonFldName, STDFlowPath.line[lineNum].STREAM_ORDER.Horton);
		TableWriteField(ResultSampleTBL, records[1], shreveFldName, STDFlowPath.line[lineNum].STREAM_ORDER.Shreve);
		TableWriteField(ResultSampleTBL, records[1], strahlerFldName, STDFlowPath.line[lineNum].STREAM_ORDER.Strahler);
		TableWriteField(ResultSampleTBL, records[1], scheideggerFldName, STDFlowPath.line[lineNum].STREAM_ORDER.Scheidegger);
	}
	#######################################################################################################
	# Caluculate the upstream channel length for each sample point
	statusC.Message = "Step 7 of 18:  Computing upstream channel length for each sample point";
	#######################################################################################################
	local numeric closestVertex, testVertex;
	local numeric distCurr2Test, distClose2Test, distCurr2Close, tempDist; 
	local numeric lengthClose2Node, lengthCurr2Node, upstreamChanLength, tempLength, junk1, junk2;
	local class POINT2D currPt, tempPt1, tempPt2, testPt, closePt;
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		lengthCurr2Node = 0;
		lengthClose2Node = 0;
		tempLength = 0;
		TableReadAttachment(ResultPointToPointTBL, i, records, "point");
		lineNum = TableReadFieldNum(ResultPointToPointTBL, onLineFldName, records[1]);
		P = GetVectorLine(STDFlowPath, lineNum);
		# Get one of the endpoints
		pt1 = P.GetVertex(0);
		ObjectToMap(STDFlowPath, pt1.x, pt1.y, FLOWgeoref, Temp1MapX, Temp1MapY);
		node1 = FindClosestNode(STDFlowPath, Temp1MapX, Temp1MapY, FLOWgeoref);
		# Node 1 needs to be upstream.  If it's not, turn the Polyline around
		if (node1 != STDFlowPath.line[lineNum].(LineToLineTblName).(upNodeFldName)) {
			P.Reverse();
			pt1 = P.GetVertex(0);
		}
		pt1 = ObjectToMap(STDFlowPath, pt1.x, pt1.y, FLOWgeoref);
		# Find the closest vertex on the line to the current sample point
		TableReadAttachment(ResultSampleTBL, i, records, "point");
		currPt.x = TableReadFieldNum(ResultSampleTBL, modEastFldName, records[1]);
		currPt.y = TableReadFieldNum(ResultSampleTBL, modNorthFldName, records[1]);
		currPt = MapToObject(FLOWgeoref, currPt.x, currPt.y, STDFlowPath);
	 	closestVertex = P.FindClosestVertex(currPt);
		currPt = ObjectToMap(STDFlowPath, currPt.x, currPt.y, FLOWgeoref);
		# If closestVertex is the upstream node, then the line distance is the distance from the current
		# point to the upstream node;
		if (closestVertex == 0) {
			Displacement3D(currPt.x, currPt.y, 0, pt1.x, pt1.y, 0, lengthCurr2Node, junk1, junk2);
		# Otherwise, need to find distance from closest vertex to the upnode +/- where the actual point is
		} else {
			# Calculate the length of the line from the closest vertex to the upstream node
			for j = 1 to closestVertex {
				tempPt1 = P.GetVertex(j-1);
				tempPt1 = ObjectToMap(STDFlowPath, tempPt1.x, tempPt1.y, FLOWgeoref);
				tempPt2 = P.GetVertex(j);
				tempPt2 = ObjectToMap(STDFlowPath, tempPt2.x, tempPt2.y, FLOWgeoref);
				Displacement3D(tempPt1.x, tempPt1.y, 0, tempPt2.x, tempPt2.y, 0, tempDist, junk1, junk2);
				lengthClose2Node = lengthClose2Node + tempDist;
			}
			closePt = P.GetVertex(closestVertex);
			closePt = ObjectToMap(STDFlowPath, closePt.x, closePt.y, FLOWgeoref);
			# If the closest vertex point is not the same as the current point, offset the vertex to node
			# length based on the distance the current point is from the closest vertex
			if (closePt != currPt) {
				# Get the vertex that is one closer to the upstream node
				testVertex = closestVertex - 1;
				testPt = P.GetVertex(testVertex);
				testPt = ObjectToMap(STDFlowPath, testPt.x, testPt.y, FLOWgeoref);
				Displacement3D(currPt.x, currPt.y, 0, testPt.x, testPt.y, 0, distCurr2Test, junk1, junk2);
				Displacement3D(closePt.x, closePt.y, 0, testPt.x, testPt.y, 0, distClose2Test, junk1, junk2);
				Displacement3D(currPt.x, currPt.y, 0, closePt.x, closePt.y, 0, distCurr2Close, junk1, junk2);
				# If the distance from the current point to the test vertex is greater then the closest to the test,
				# then the additional distance needs to be added.
				if (distCurr2Test > 	distClose2Test) {
					lengthCurr2Node = lengthClose2Node + distCurr2Close;
				# Otherwise, the distance needs to be subtracted
				} else {
					lengthCurr2Node = lengthClose2Node - distCurr2Close;
				}
			} else {
				lengthCurr2Node = lengthClose2Node;
			}
		}
		# Get the total upstream channel length that is upstream of this line
		TableReadAttachment(STDFlowPathTBL, lineNum, records, "line");
		upstreamChanLength = TableReadFieldNum(STDFlowPathTBL, totalUpChanLengthFldName, records[1]);
		# Compute the total upstream channel length from the sample point in km
		tempLength = upstreamChanLength + (lengthCurr2Node / 1000);
		# Write the length to the record
		TableReadAttachment(ResultSampleTBL, i, records, "point");
		TableWriteField(ResultSampleTBL, records[1], totalUpChanLenFldName, tempLength);
	} # End for i loop
	#######################################################################################################
	# Create relationships between sample points that are on the same line based upon their location
	# upstream and downstream of each other
	statusC.Message = "Step 8 of 18:  Computing relationships between sample points on same flowpath line";
	#######################################################################################################
	local numeric currVertex, tempVertex;
	local numeric closestSample, closestDist, distApart; 
	local numeric numOnSameLine, thisLineNum;
	local numeric tempDist, currDist, closeDist;
	local array numeric onSameLine[10]; # Currently allows up to 10 points on the same line
	# Create relationships for points that ARE on the same line as others
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		thisLineNum = ResultVECT.point[i].PointToPoint.(onLineFldName);
		closestDist = -1;
		closestSample = -1;
		closestVertex = -1;
		# Search for any points that are on the same line
		for j = 1 to numSamplePoints {
			# Reset variables
			numOnSameLine = 0;
			for k = 1 to 10 {
				onSameLine[k] = -1;
			}
			# Ignore itself
			if (j != i) {
				# Check if the other point is on the same line
				if (thisLineNum == ResultVECT.point[j].PointToPoint.(onLineFldName)) {
					numOnSameLine = numOnSameLine + 1;
					onSameLine[numOnSameLine] = j;
				}
			}
			# If there are points on the same line
			if (numOnSameLine > 0) {
				# Get one of the end nodes and check if it is upstream
				P = GetVectorLine(STDFlowPath, thisLineNum);
				# Get one of the endpoints
				pt1 = P.GetVertex(0);
				ObjectToMap(STDFlowPath, pt1.x, pt1.y, FLOWgeoref, Temp1MapX, Temp1MapY);
				node1 = FindClosestNode(STDFlowPath, Temp1MapX, Temp1MapY, FLOWgeoref);
				# Node 1 needs to be downstream.  If it's not, turn the Polyline around
				if (node1 != STDFlowPath.line[thisLineNum].(LineToLineTblName).(downNodeFldName)) {
					P.Reverse();
					pt1 = P.GetVertex(0);
				}
				# Find the closest vertex on the line to the current sample point
				TableReadAttachment(ResultSampleTBL, i, records, "point");
				currPt.x = TableReadFieldNum(ResultSampleTBL, modEastFldName, records[1]);
				currPt.y = TableReadFieldNum(ResultSampleTBL, modNorthFldName, records[1]);
				currPt = MapToObject(FLOWgeoref, currPt.x, currPt.y, STDFlowPath);
				currVertex = P.FindClosestVertex(currPt);
				for k = 1 to numOnSameLine {
					TableReadAttachment(ResultSampleTBL, onSameLine[k], records, "point");
					tempPt1.x = TableReadFieldNum(ResultSampleTBL, modEastFldName, records[1]);
					tempPt1.y = TableReadFieldNum(ResultSampleTBL, modNorthFldName, records[1]);
					tempPt1 = MapToObject(FLOWgeoref, tempPt1.x, tempPt1.y, STDFlowPath);
					tempVertex = P.FindClosestVertex(tempPt1);
					# Notify user if sample points are located at the same place.  Shouldn't happen.
					if (tempPt1 == currPt) {
						TableReadAttachment(ResultSampleTBL, onSameLine[k], records, "point");
						tempID1 = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
						TableReadAttachment(ResultSampleTBL, i, records, "point");
						tempID2 = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
						message = "Warning:  Sample point " + tempID1 + " has the same location as " + tempID2 + " \n" +
						          "          It is best that these points be edited and then run again.\n\n";
						print(message);
						fwritestring(summaryFile, message);
					# Check if tempVertex could be closer then closestVertex and is the same or less then the current
					} else if ( (tempVertex >= closestVertex) and (tempVertex <= currVertex) ) {
						# If the temp Vertex is the same as the current Vertex, check if temp is downstream
						if (tempVertex == currVertex) {
							# Use the next vertex downstream for the test
							testVertex = currVertex - 1;
							# Make sure the next vertex downstream is valid.  Can't be less then 0 or the
							# point would be on the next downstream line.
							if (testVertex < 0) {
								testVertex = 0;
							}
							# Determine the distance from each point and the distances to the next downtream node
							testPt = P.GetVertex(testVertex);
							Displacement3D(currPt.x, currPt.y, 0, testPt.x, testPt.y, 0, currDist, junk1, junk2);
							Displacement3D(tempPt1.x, tempPt1.y, 0, testPt.x, testPt.y, 0, tempDist, junk1, junk2);
							Displacement3D(currPt.x, currPt.y, 0, tempPt1.x, tempPt1.y, 0, distApart, junk1, junk2);
							# If the temp point is closer to the downstream vertex
							if (tempDist < currDist) {
								# If this is the first temp at this vertex
								if (closestDist == -1) {
									closestDist = distApart;
									closestSample = onSameLine[k];
									closestVertex = tempVertex;		 
								# If another point downstream had the same vertex, but this one is closer
								} else if ( (closestDist != -1) and (distApart < closestDist) ) {
									closestDist = distApart;
									closestSample = onSameLine[k];
									closestVertex = tempVertex;		 
								}
							}
						# If the temp Vertex is downstream of the current Vertex
						} else if (tempVertex < currVertex) {
							# If the temp Vertex is the same as the closest vertex, find out which is further upstream
							if (tempVertex == closestVertex) {
								# Use the next vertex upstream as a test
								testVertex = closestVertex + 1;
								# Get the current closests points coordinates
								TableReadAttachment(ResultSampleTBL, closestSample, records, "point");
								closePt.x = TableReadFieldNum(ResultSampleTBL, modEastFldName, records[1]);
								closePt.y = TableReadFieldNum(ResultSampleTBL, modNorthFldName, records[1]);
								closePt = MapToObject(FLOWgeoref, closePt.x, closePt.y, STDFlowPath);
								# Determine the distance from each point and the distances to the next uptream node
								testPt = P.GetVertex(testVertex);
								Displacement3D(closePt.x, closePt.y, 0, testPt.x, testPt.y, 0, closeDist, junk1, junk2);
								Displacement3D(tempPt1.x, tempPt1.y, 0, testPt.x, testPt.y, 0, tempDist, junk1, junk2);
								Displacement3D(closePt.x, closePt.y, 0, tempPt1.x, tempPt1.y, 0, distApart, junk1, junk2);
								# If the temp point is closer to the upstream vertex, then it is further upstream
								if (tempDist < closeDist) {
									closestSample = onSameLine[k];
									closestVertex = tempVertex;		 
								}
							# If temp Vertex is greater then closest Vertex, then it is closer to the current point
							} else if (tempVertex > closestVertex) {
								closestSample = onSameLine[k];
								closestVertex = tempVertex;		 
							}
						} # End (temp vs curr)
					} # End (temp >= closest)
				} # End for loop
				# Recored the sample point number found upstream
				if (closestVertex != -1) {
					TableReadAttachment(ResultSampleTBL, closestSample, records, "point");
					sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
					TableReadAttachment(ResultPointToPointTBL, i, records, "point");
					TableWriteField(ResultPointToPointTBL, records[1], downSampleFldName, sampleNum$);
				}
			} # End "For each on same line"
		} #End "On Same Line"
	}
	#######################################################################################################
	# Determine the most upstream and downstream sample points on each line segment
	statusC.Message = "Step 9 of 18:  Computing the most upstream and downstream sample points for each flowpath line";
	#######################################################################################################
	local array numeric pointsOnLine[100];
	local numeric numPointsOnLine, relationshipExists;
	local string currSampleNum$;
	# Determine the most upstream and most downstream sample points on each line
	statusC.BarInit(numFlowPathLines, 0);
	for i = 1 to numFlowPathLines {
		statusC.BarIncrement(1, 0);
		numPointsOnLine = 0;
		# For each sample point
		for j = 1 to numSamplePoints {
			# If the sample point is on the current line, add it to the array
			if (ResultVECT.point[j].PointToPoint.(onLineFldName) == i) {
				numPointsOnLine = numPointsOnLine + 1;
				pointsOnLine[numPointsOnLine] = j;
			}
		} 
		# If the line has no sample points on it, leave the fields blank
		if (numPointsOnLine == 0) {
			# Assign it a value of nothing, can be changed to a unique identifier string
			TableReadAttachment(STDFlowPathTBL, i, records, "line");
			TableWriteField(STDFlowPathTBL, records[1], mostDownSampleFldName, "");
			TableWriteField(STDFlowPathTBL, records[1], mostUpSampleFldName, "");
		# If there is only one point on the line, it is both the most upstream and downstream sample
		} else if (numPointsOnLine == 1) {
			TableReadAttachment(ResultSampleTBL, pointsOnLine[1], records, "point");
			sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
			TableReadAttachment(STDFlowPathTBL, i, records, "line");
			TableWriteField(STDFlowPathTBL, records[1], mostDownSampleFldName, sampleNum$);
			TableWriteField(STDFlowPathTBL, records[1], mostUpSampleFldName, sampleNum$);
		# If there are more then 1 sample points on the line
		} else if (numPointsOnLine > 1) {
			# For each point on the line, determine it's most downstream sample
			for j = 1 to numPointsOnLine {
				# If the current point on the line doesn't have a Downstream Sample, it is the most downstream
				TableReadAttachment(ResultPointToPointTBL, pointsOnLine[j], records, "point");
				sampleNum$ = TableReadFieldStr(ResultPointToPointTBL, downSampleFldName, records[1]);
 
				if (sampleNum$.length == 0) {
					# Get it's real sample number and set it as the most downstream sample for this line
					TableReadAttachment(ResultSampleTBL, pointsOnLine[j], records, "point");
					sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
					TableReadAttachment(STDFlowPathTBL, i, records, "line");
					TableWriteField(STDFlowPathTBL, records[1], mostDownSampleFldName, sampleNum$);
				}
			}
			# For each point on the line, determine it's most upstream sample
			for j = 1 to numPointsOnLine {
				# If no points relate to it as being downstream, it is the most upstream in the line
				relationshipExists = 0;
				TableReadAttachment(ResultSampleTBL, pointsOnLine[j], records, "point");
				currSampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
				# Check the other points on the line to see if the point to it as being downstream
				for k = 1 to numPointsOnLine {
					# Don't check itself
					if (k != j) {
						TableReadAttachment(ResultPointToPointTBL, pointsOnLine[k], records, "point");
						sampleNum$ = TableReadFieldStr(ResultPointToPointTBL, downSampleFldName, records[1]);
						# If something is pointing to it as downstream, it isn't the most upstream
						if (sampleNum$ == currSampleNum$) {
							relationshipExists = 1;
						}
					} 
				}
				# If no relationship exists on this line, it is the most upstream point on that line
				if (!relationshipExists) {
					TableReadAttachment(ResultSampleTBL, pointsOnLine[j], records, "point");
					sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
					TableReadAttachment(STDFlowPathTBL, i, records, "line");
					TableWriteField(STDFlowPathTBL, records[1], mostUpSampleFldName, sampleNum$);
				}
			} #End For loop
		} # End comparision of numPointsOnLine
	} # End for loop
	#######################################################################################################
	# Determine the relationships for sample points that require traversing a seperate line to find their
	# immediate downstream or multiple upstream sample points
	statusC.Message = "Step 10 of 18:  Searching for downstream sample points for each sample";
	#######################################################################################################
	local numeric currLineNum, downstreamLineNum, stillSearching;
	# Find the downstream relationships first
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		TableReadAttachment(ResultPointToPointTBL, i, records, "point");
		sampleNum$ = TableReadFieldStr(ResultPointToPointTBL, downSampleFldName, records[1]);
		# If the sample doesn't currently have a downstream sample, it needs to find one
		if (sampleNum$.length == 0) {
			TableReadAttachment(ResultPointToPointTBL, i, records, "point");
			currLineNum = TableReadFieldNum(ResultPointToPointTBL, onLineFldName, records[1]);
			TableReadAttachment(STDFlowPathTBL, currLineNum, records, "line");
			downstreamLineNum = TableReadFieldNum(STDFlowPathTBL, downLineFldName, records[1]);
			# Check if the current line has a downstream line.  If so, traverse downstream until we find a 
			# line with an UpstreamSample, if it exists
			if (downstreamLineNum != -1) {
				stillSearching = 1;
				while (stillSearching) {
					TableReadAttachment(STDFlowPathTBL, downstreamLineNum, records, "line");
					sampleNum$ = TableReadFieldStr(STDFlowPathTBL, mostUpSampleFldName, records[1]);
					# If the downstream line has a Upstream Sample, attach it as the downstream sample
					if (sampleNum$.length != 0) {
						# Write the record and stop searching
						TableReadAttachment(ResultPointToPointTBL, i, records, "point");
						TableWriteField(ResultPointToPointTBL, records[1], downSampleFldName, sampleNum$);
						stillSearching = 0;
					# Otherwise, check to see if the next downstream line exists
					} else {
						currLineNum = downstreamLineNum;
						TableReadAttachment(STDFlowPathTBL, currLineNum, records, "line");
						downstreamLineNum = TableReadFieldNum(STDFlowPathTBL, downLineFldName, records[1]);
						# If it doesn't exist, stop searching for it
						if (downstreamLineNum == -1) {
							stillSearching = 0;
						} 
						# If it does exist, repeat the while loop to search that downstream line
					} # End sampleNum$.length
				} # End while loop
			} # End initial downstream test
		} # End downstream sample check
	} # End for each point loop
	local numeric currMaxNumUpstreamSampleFields = 2;
	local array numeric upstreamSamples[numSamplePoints];
	local numeric numUpstreamSamples;
	# Find the upstream relationships between sample points
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		numUpstreamSamples = 0;
		TableReadAttachment(ResultSampleTBL, i, records, "point");
		currSampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
		# Check all samples to see if they list it as downstream of them
		for j = 1 to numSamplePoints {
			# Don't bother check itself
			if (j != i) {
				TableReadAttachment(ResultPointToPointTBL, j, records, "point");
				sampleNum$ = TableReadFieldStr(ResultPointToPointTBL, downSampleFldName, records[1]);
				# If the j sample point's downstream sample is i, then save j to the array
				if (sampleNum$ == currSampleNum$) {
					numUpstreamSamples = numUpstreamSamples + 1;
					upstreamSamples[numUpstreamSamples] = j;
				} # End sample comparison
			} # End if not itself
		} # End for j loop
		# If there aren't enough upstream sample fields in the table, create the additional number needed
		while (currMaxNumUpstreamSampleFields < numUpstreamSamples) {
			currMaxNumUpstreamSampleFields = currMaxNumUpstreamSampleFields + 1;
			tempFieldName = upSampleFldName + NumToStr(currMaxNumUpstreamSampleFields);
			TableAddFieldString(ResultPointToPointTBL, tempFieldName, 11);
		}
		TableReadAttachment(ResultPointToPointTBL, i, records, "point");
		TableWriteField(ResultPointToPointTBL, records[1], numUpSamplesFldName, numUpstreamSamples);
		# Add each upstream sample number to the current samples database table
		for j = 1 to numUpstreamSamples {
			# Get the upstream sample number
			TableReadAttachment(ResultSampleTBL, upstreamSamples[j], records, "point");
			sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
			# Add the sample number as being upstream in the appropriate field
			TableReadAttachment(ResultPointToPointTBL, i, records, "point");
			tempFieldName = upSampleFldName + NumToStr(j);
			TableWriteField(ResultPointToPointTBL, records[1], tempFieldName, sampleNum$);
		} # End for j loop
	} # End for i loop
	#######################################################################################################
	# Find the total number of upstream sample points for each sample point
	statusC.Message = "Step 11 of 18:  Computing total number of upstream sample points for each sample";
	#######################################################################################################
	local numeric totalUpstream, numUpstream, upstreamSampleTotal, currTotalUpSamples;
	numComputed = 0;
	tempCount = 0;
	# Initialize the total number of upstream samples field by setting it to 0 if there are non upstream,
	# or to -1 to specify that it needs to be computed.
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		TableReadAttachment(ResultPointToPointTBL, i, records, "point");
		totalUpstream = TableReadFieldNum(ResultPointToPointTBL, numUpSamplesFldName, records[1]);
		if (totalUpstream == 0) {
			numComputed = numComputed + 1;
			TableWriteField(ResultPointToPointTBL, records[1], totalUpSamplesFldName, 0);
		} else {
			TableWriteField(ResultPointToPointTBL, records[1], totalUpSamplesFldName, -1);
		}
	}
	# Cycle through all the points continually until they all have the correct number of total upstream samples
	statusC.BarInit(numSamplePoints, 0);
	while (numComputed > 0 ) {
		# Reset the while counter
		tempCount += numComputed;
		statusC.BarIncrement(numComputed, 0);
		numComputed = 0;
		for i = 1 to numSamplePoints {
			# Check the number of total upstream samples
			TableReadAttachment(ResultPointToPointTBL, i, records, "point");
			totalUpstream = TableReadFieldNum(ResultPointToPointTBL, totalUpSamplesFldName, records[1]);
			# If -1, it still needs to be computed
			if (totalUpstream == -1) {
				# Find the number of immediate upstream samples and add it to the current total
				numUpstream = TableReadFieldNum(ResultPointToPointTBL, numUpSamplesFldName, records[1]);
				currTotalUpSamples = numUpstream;
				# Initialize the list and then get the list of the upstream sample numbers
				UpSample.Clear();
				UpSample.AddToFront("");
				for j = 1 to numUpstream {
					UpSample.AddToEnd(TableReadFieldStr(ResultPointToPointTBL, upSampleFldName + NumToStr(j), records[1]));
				}
				for j = 1 to numUpstream {
					# If the curret Total is not invalid, keep checking upstream samples
					if (currTotalUpSamples != -1) {
						# Get the point element the Sample Number refers to and check it's total upstream samples field
						records[1] = TableKeyFieldLookup(ResultSampleTBL, sampleIDFldName, UpSample[j], "Equals");
						TableGetRecordElementList(ResultSampleTBL, records[1], elements, "point");
						TableReadAttachment(ResultPointToPointTBL, elements[1], records, "point");
						upstreamSampleTotal = TableReadFieldNum(ResultPointToPointTBL, totalUpSamplesFldName, records[1]);
						# If the upstream sample point of i has a total number of sample points, add it to the running total
						if (upstreamSampleTotal != -1) {
							currTotalUpSamples = currTotalUpSamples + upstreamSampleTotal;
						# The upstream samples have yet to be calculated, set the current total to invalid
						} else {
							currTotalUpSamples = -1;
						}
					} 
				} # End for j loop
				# If a valid current total of upstream samples was collected, record it to the table for that point
				if (currTotalUpSamples != -1) {
					TableReadAttachment(ResultPointToPointTBL, i, records, "point");
					TableWriteField(ResultPointToPointTBL, records[1], totalUpSamplesFldName, currTotalUpSamples);
					# Update the while counter so it will run again
					numComputed = numComputed + 1;
				}
			} # End if
		} # End for i loop
	} # End while loop
	# Record the number of sample upstream to the point sediment sample database
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		TableReadAttachment(ResultPointToPointTBL, i, records, "point");
		totalUpstream = TableReadFieldNum(ResultPointToPointTBL, totalUpSamplesFldName, records[1]);
		TableReadAttachment(ResultSampleTBL, i, records, "point");
		TableWriteField(ResultSampleTBL, records[1], totalUpSamplesFldName, totalUpstream);
	}
	#######################################################################################################
	# Use the new sample point locations to compute a new user defined watershed
	statusC.Message = "Step 12 of 18  Computing user defined Watershed objects";
	statusC.BarClear(0);
	#######################################################################################################
	# Recreate the watershed using the new modified points
	string watershedparms$ = "";
	if ( mainDLG.GetCtrlByID("id_FILL_ALL_DEP").GetValueNum()) {
		watershedparms$ += "FillAllDepressions,";
	}
	if ( mainDLG.GetCtrlByID("id_COMP_USR_FLOWPATH").GetValueNum()) {
		watershedparms$ += "FlowPath,";
	}
#	if ( mainDLG.GetCtrlByID("id_COMP_STD_DBASE").GetValueNum()) {
#		watershedparms$ += "Database,";
#	}
	watershedparms$ += "Basin";
	WatershedComputeElements(watershed, x, y, numSamplePoints, watershedparms$);
	if ( mainDLG.GetCtrlByID("id_COMP_USR_FLOWPATH").GetValueNum()) {
		WatershedGetObject(watershed, "VectorUserFlowPath", ObjectFilename, ObjectName);
		ObjNumber = ObjectNumber(ObjectFilename, ObjectName, "VECTOR");
		CopyObject(ObjectFilename, ObjNumber, OutputProjectFile);
	}
	WatershedGetObject(watershed, "VectorUserBasin", ObjectFilename, ObjectName);
	ObjNumber = ObjectNumber(ObjectFilename, ObjectName, "VECTOR");
	CopyObject(ObjectFilename, ObjNumber, OutputProjectFile);
	# Open the "VectorUserBasin" and get it's georef
	OpenVector(UBasinVECT, OutputProjectFile, "USDBASIN");
	local class GEOREF UBSNgeoref = GetLastUsedGeorefObject(UBasinVECT);
	#######################################################################################################
	# Attach the user basins with the appropriate sample points that are within the basin
	statusC.Message = "Step 13 of 18:  Attaching catchment basin polygons to appropriate sample points";
	statusC.BarClear(0);
	#######################################################################################################
	local numeric numBasinsComputed = 0;
	local numeric numUserBasins = NumVectorPolys(UBasinVECT);
	local numeric polyNum;
	local class DATABASE UBasinDB = OpenVectorPolyDatabase(UBasinVECT);
	# Copy the sample database table from original vector
	if (TableCopy(SampleDB, SampleTBL, UBasinDB) < 0) {
		message = "Error occured while trying to copy the sample table.\n" + "Exiting the script now... \n";
		print(message);
		fwritestring(summaryFile, message);
		CloseVector(UBasinVECT);
		CloseRaster(SampleDEM);
		CloseVector(SampleVECT);
		CloseVector(WarpVECT);
		CloseVector(ResultVECT);
		return;
	}
	local class DBTABLEINFO UBasinSampleTBL = DatabaseGetTableInfo(UBasinDB, sampleTblName);
	UBasinSampleTBL.OneRecordPerElement = 1;
	local class DBTABLEINFO UBasinPolyToPolyTBL = TableCreate(UBasinDB, PolyToPolyTblName, PolyToPolyTblDesc);
	UBasinPolyToPolyTBL.OneRecordPerElement = 1;
	TableAddFieldString(UBasinPolyToPolyTBL, sampleIDFldName, 12);					# Sample ID related to the basin polygon
	TableAddFieldInteger(UBasinPolyToPolyTBL, numPolysPerSampleFldName, 15);		# Number of vector polygons related to that sample
	TableAddFieldFloat(UBasinPolyToPolyTBL, areaSamplePolysFldName, 15, 4);			# Total of all vector polygons related to that sample
 
	# Create a record in the table for each basin
	statusC.BarInit(numUserBasins, 0);
	for i = 1 to numUserBasins {
		statusC.BarIncrement(1, 0);
		records[1] = TableWriteRecord(UBasinPolyToPolyTBL, 0, "", 1, -1);
		TableWriteAttachment(UBasinPolyToPolyTBL, i, records, 1, "polygon");
if (DEBUG) {
	fwritestring(summaryFile, "   i:" + NumToStr(i) + "   rec[1]: " + NumToStr(records[1]) + "\n");
}
	}
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		Temp1ObjX = ResultVECT.Point[i].Internal.x;  # In object coordinates
		Temp1ObjY = ResultVECT.Point[i].Internal.y;
		ObjectToMap(ResultVECT, Temp1ObjX, Temp1ObjY, RSLTgeoref, Temp1MapX, Temp1MapY);
		polyNum = FindClosestPoly(UBasinVECT, Temp1MapX, Temp1MapY, UBSNgeoref);
		# If a polygon was found, save the sample name to the polygon table
		if (polyNum > 0) {
			TableReadAttachment(ResultSampleTBL, i, records, "point");
			sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
			TableReadAttachment(UBasinPolyToPolyTBL, polyNum, records, "polygon");
			TableWriteField(UBasinPolyToPolyTBL, records[1], sampleIDFldName, sampleNum$);
			TableWriteField(UBasinPolyToPolyTBL, records[1], numPolysPerSampleFldName, 1);
		# Warning if no polygon is found.  This shouldn't happen.
		} else {
			TableReadAttachment(ResultSampleTBL, i, records, "point");
			tempID1 = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
			message = "Warning:  The sample point with the element number " + NumToStr(i) + " was not able to be associated\n" +
			          "          with one of the polygons.  Please examine the results to confirm that the\n" +
			          "          sample point does not fall within any of the user defined basin polygons.\n\n";
			print(message);
			fwritestring(summaryFile, message);
		}
	}
	#######################################################################################################
	# Remove records for basins that are not attached to any sample point, and warn the user of them.
	statusC.Message = "Step 14 of 18:  Removing unattached polygon records";
	#######################################################################################################
	numRecords = NumRecords(UBasinPolyToPolyTBL);
	# Scan through the records.  If it doesn't have a sample number, it shouldn't be a watershed basin.
	# (Some extra polygons are created due to multiple polygons created around it)
	statusC.BarInit(numRecords, 0);
	for i = 1 to NumRecords(UBasinPolyToPolyTBL) {
		statusC.BarIncrement(1, 0);
		# Check if it has a sample number attached
		sampleNum$ = TableReadFieldStr(UBasinPolyToPolyTBL, sampleIDFldName, i);
if (DEBUG) {
	fwritestring(summaryFile, "   i:" + NumToStr(i) + "   samp$:" + sampleNum$ + "\n");
}
		# If it doesn't, get the polygon element and remove its attachment to the record
		if (sampleNum$.length == 0) {
numeric tempnum;
			tempnum = TableGetRecordElementList(UBasinPolyToPolyTBL, i, elements, "polygon");
			records[1] = i;
			TableRemoveAttachment(UBasinPolyToPolyTBL, elements[1], records, 1, "polygon"); 
if (DEBUG) {
	fwritestring(summaryFile, "      rec[1]:" + NumToStr(records[1]) + "   tempN:" + NumToStr(tempnum) + "   elem[1]:" + NumToStr(elements[1]) + "\n");
}
			warnCountLostPolygons = warnCountLostPolygons + 1;
			message = "Warning:  The following polygon element " + NumToStr(elements[1]) + " does not have a sample point attached\n" + 
			          "          to it.  This could be a result of a squeeze point in the watershed basins\n" +
			          "          vector.  Modification of the original point may be required.\n\n";
			print(message);
			fwritestring(summaryFile, message);
		}
	}
	# Remove all the unattached records
	TableRemoveUnattachedRecords(UBasinPolyToPolyTBL);
	message = "##########################################################################################\n" +
	          "Number of unidentified polygons found: " + NumToStr(warnCountLostPolygons) + "\n" +
	          "##########################################################################################\n\n";
	print(message);
	fwritestring(summaryFile, message);
	#######################################################################################################
	# Iterate through basin polygons that have no attached sample record; trace flow directions downstream from a 
	# cell inside the basin to find if the basin connects to an attributed downstream basin. If so, assign
	# the associated sample record to the current polygon.
	# This solves the case in which a catchment basin is "pinched" due to a diagonal
	# flowpath line, creating separate basin polygons connected only by a node.
	statusC.Message = "Step 15 of 18:  Searching for relevant unattached polygons";
	#######################################################################################################
	local numeric m, n, t, u,  tempValx, tempValy, stillPassingTest, numPolys;
	local numeric numNodes, numAdjPolys, notInList, relationshipFound, sampleLineNum;
	local array numeric adjPolyList[1];
	local class POLYLINE currPlineOfNodes, tempPlineOfNodes;
	local numeric temp1ClosestVertex, temp2ClosestVertex, dist1, dist2;
	local class POINT2D sharedNodePt;
	# While the there is a possibility for additional basins to be attached
	WatershedGetObject(watershed, "RasterFlowDirection", ObjectFilename, ObjectName);
	class RVC_RASTER FlowDirs;
	OpenRaster(FlowDirs, ObjectFilename, ObjectName);
	numeric nullval = NullValue(FlowDirs);
	numBasinsComputed = 1;
	tempCount = 0;
	statusC.BarInit(warnCountLostPolygons, 0);
	while (numBasinsComputed > 0) {
		tempCount += numBasinsComputed;
		statusC.BarIncrement(numBasinsComputed, 0);
		numBasinsComputed = 0;
		# Find any basins without sample point ID's that should have one, and give them an ID
		for i = 1 to numUserBasins {
			numRecords = TableReadAttachment(UBasinPolyToPolyTBL, i, records, "polygon");
			# If the polygon is not currently attached to any sample point
			if (numRecords == 0) {
				array numeric UnattachedPolys[warnCountLostPolygons];
				numeric index = 1;
				for index = 1 to warnCountLostPolygons {
					UnattachedPolys[index] = -1;
					}
				index = 1;
				UnattachedPolys[index] = i;
				relationshipFound = 0;
				class POLYLINE poly = GetVectorPoly(UBasinVECT, i);
				class POINT2D vertex = poly.GetVertex(1);
				vertex.x += .5;
				vertex.y -= .5;
				if (!poly.IsPointInside(vertex)) {
					vertex = poly.GetVertex(1);
					vertex.x -= .5;
					vertex.y -= .5;
					}
				if (!poly.IsPointInside(vertex)) {
					vertex = poly.GetVertex(1);
					vertex.x -= .5;
					vertex.y += .5;
					}
				if (!poly.IsPointInside(vertex)) {
					vertex = poly.GetVertex(1);
					vertex.x += .5;
					vertex.y += .5;
					}
				while (true) {
					class POINT2D FlowCell = ObjectToMap(UBasinVECT, vertex.x, vertex.y, UBSNgeoref);
 					FlowCell = MapToObject(UBSNgeoref, FlowCell.x, FlowCell.y, FlowDirs);
					numeric FlowValue = FlowDirs[floor(FlowCell.y), floor(FlowCell.x)];
					if (FlowValue == nullval) break;
					if (IsNull(FlowValue)) break;
					switch (FlowValue) {
						case 1: # upper right
							vertex.x += 1;
							vertex.y += 1;
							break;
						case 2: # right
							vertex.x += 1;
							break;
						case 3: # lower right
							vertex.x += 1;
							vertex.y -= 1;
							break;
						case 4: # lower
							vertex.y -= 1;
							break;
						case 5: # lower left
							vertex.x -= 1;
							vertex.y -= 1;
							break;
						case 6: # left
							vertex.x -= 1;
							break;
						case 7: # upper left
							vertex.x -= 1;
							vertex.y += 1;
							break;
						case 8: # upper
							vertex.y += 1;
							break;
						}
					numeric AdjPoly = FindClosestPoly(UBasinVECT, vertex.x, vertex.y);
					class POLYLINE AdjPolyline = GetVectorPoly(UBasinVECT, AdjPoly);
					if (AdjPoly == 0 || !AdjPolyline.IsPointInside(vertex)) {
						break;
						}
					if (AdjPoly != UnattachedPolys[index]) {
						numRecords = TableReadAttachment(UBasinPolyToPolyTBL, AdjPoly, records, "polygon");
						if (numRecords > 0) {
							relationshipFound = AdjPoly;
							break;
							}
						else {
							UnattachedPolys[++index] = AdjPoly;
							}
						}
					}
				# If a relationship was found, record it
				if (relationshipFound) {
					# Attach the polygon to the record that has the correct sample point ID
					TableReadAttachment(UBasinPolyToPolyTBL, relationshipFound, records, "polygon");
					for index = 1 to 100 {
						if (UnattachedPolys[index] == -1) break;
						TableWriteAttachment(UBasinPolyToPolyTBL, UnattachedPolys[index], records, 1, "polygon");
						# Add 1 to the number of polygons previously attached to the sample point
						numPolys = TableReadFieldNum(UBasinPolyToPolyTBL, numPolysPerSampleFldName, records[1]) + 1;
						TableWriteField(UBasinPolyToPolyTBL, records[1], numPolysPerSampleFldName, numPolys);
						message = "Correction:  The following polygon element " + NumToStr(UnattachedPolys[index]) + " does not have a sample point inside of\n" + 
					          "             it, but since it was connected to an attributed downstream polygon, it was attached to that sample point.\n\n";
						print(message);
						fwritestring(summaryFile, message);
						# Increment the counters
						numBasinsComputed = numBasinsComputed + 1;
						warnCountManAttachPolygons = warnCountManAttachPolygons + 1;
					}
				}
			} # End if i doesn't have a sampleID
		} # End for i loop
	} # End While loop
	message = "##########################################################################################\n" +
	          "Number of manually attached polygons found: " + NumToStr(warnCountManAttachPolygons) + "\n" +
	          "##########################################################################################\n\n";
	print(message);
	fwritestring(summaryFile, message);
	#######################################################################################################
	# Attach the user defined catchment basin polygons to the copied sediment sample table
	statusC.Message = "Step 16 of 18:  Attatching polygons to the sediment sample database";
	#######################################################################################################
	statusC.BarInit(numUserBasins, 0);
	for i = 1 to numUserBasins {
		statusC.BarIncrement(1, 0);
		#  If the polygon is attached to a sample in the PolyToPoly Table, transfer that attachment to the sample table
		if (TableReadAttachment(UBasinPolyToPolyTBL, i, records, "polygon") > 0) {
			sampleNum$ = TableReadFieldStr(UBasinPolyToPolyTBL, sampleIDFldName, records[1]);
			# Make sure the basin has a sample point
			if (sampleNum$.length != 0) {
				records[1] = TableKeyFieldLookup(UBasinSampleTBL, sampleIDFldName, sampleNum$, "Equals");
				TableWriteAttachment(UBasinSampleTBL, i, records, 1, "polygon");
			}
		}
	}
	#######################################################################################################
	# Calculate the statistics for the combined upstream basins for each point
	statusC.Message = "Step 17 of 18:  Computing upstream area statistics for each catchment basin";
	#######################################################################################################
	TableAddFieldInteger(UBasinPolyToPolyTBL, totalSamplesIncludedFldName, 15);		# Number of Samples included (self included)
	TableAddFieldFloat(UBasinPolyToPolyTBL, totalSampleBasinAreaFldName, 15, 4);		# Total area of all polygons upstream of sample point (self included)
	TableAddFieldFloat(UBasinSampleTBL, totalSampleBasinAreaFldName, 15, 4);			# Total area of all polygons upstream of sample point (self included)
	TableAddFieldInteger(UBasinPolyToPolyTBL, totalSampleBasinsFldName, 12);			# Total number of sample basins for sample point (self included)
	TableAddFieldInteger(UBasinPolyToPolyTBL, numUpBasinsFldName, 12);					# Number of basins upstream (self excluded)
	local numeric numUpSamples, totalUpSamples, tempAreaValue, numElements;
	local numeric totalInclArea, currTotalArea, upstreamSamplesTotalArea;
if (DEBUG) {
	statusC.Message = "Step 17 of 18:  Computing upstream area statistics for each catchment basin (FIRST LOOP)";
	fwritestring(summaryFile, "Step 17 of 18:  Computing upstream area statistics for each catchment basin (FIRST LOOP)" + "\n");
#	PopupMessage("Start debugging here");
}
	# Determine the area of the basin for each sample point.  This will be the sum of all basins with
	# the same sample point ID
	statusC.BarInit(numUserBasins, 0);
	for i = 1 to numUserBasins {
		statusC.BarIncrement(1, 0);
		tempnum = TableReadAttachment(UBasinPolyToPolyTBL, i, records, "polygon");
		sampleNum$ = TableReadFieldStr(UBasinPolyToPolyTBL, sampleIDFldName, records[1]);
if (DEBUG) {
	fwritestring(summaryFile, "   i:" + NumToStr(i) + "  numS: " + sampleNum$ + "   tempN:" + NumToStr(tempnum) + "   rec[1]:" + NumToStr(records[1]) + "\n");
}
		# Make sure the basin has a sample point
		if (sampleNum$.length != 0) {
			# Check if the basin has it's sample point area computed yet
			tempAreaValue = TableReadFieldNum(UBasinPolyToPolyTBL, areaSamplePolysFldName, records[1]);
if (DEBUG) {
	fwritestring(summaryFile, "      tempA:" + NumToStr(tempAreaValue) + "\n");
}
			# If it is invalid, it needs to compute it
			if (tempAreaValue == -1) {
				# Find common record for the polygons attached to the current sample point
				records[1] = TableKeyFieldLookup(UBasinPolyToPolyTBL, sampleIDFldName, sampleNum$, "Equals");
if (DEBUG) {
	fwritestring(summaryFile, "         rec[1]:" + NumToStr(records[1]) + "\n");
}
				tempAreaValue = 0;
				# For all the polygons attached to that sample point, sum up their areas in square km
				numElements = TableGetRecordElementList(UBasinPolyToPolyTBL, records[1], elements, "polygon");
				for j = 1 to numElements {
					tempAreaValue = tempAreaValue + (UBasinVECT.poly[elements[j]].POLYSTATS.Area / 1000000);
				}
if (DEBUG) {
	fwritestring(summaryFile, "         tempA:" + NumToStr(tempAreaValue) + "   numE:" + NumToStr(numElements) + "\n");
}
				# Record the value back to the record for the polygons
				TableWriteField(UBasinPolyToPolyTBL, records[1], areaSamplePolysFldName, tempAreaValue);
			}
		}
	}
	numBasinsComputed = 0;
if (DEBUG) {
	statusC.Message = "Step 17 of 18:  Computing upstream area statistics for each catchment basin (SECOND LOOP)";
	fwritestring(summaryFile, "Step 17 of 18:  Computing upstream area statistics for each catchment basin (SECOND LOOP)" + "\n");
}
	# Copy the number of upstream samples for each sample to the polygon table, same as number of basins
	statusC.BarInit(numSamplePoints, 0);
	for i = 1 to numSamplePoints {
		statusC.BarIncrement(1, 0);
		# Get the sample name and number of upstream samples for the current sample point
		TableReadAttachment(ResultSampleTBL, i, records, "point");
		sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
		TableReadAttachment(ResultPointToPointTBL, i, records, "point");
		totalUpSamples = TableReadFieldNum(ResultPointToPointTBL, totalUpSamplesFldName, records[1]);
		numUpSamples = TableReadFieldNum(ResultPointToPointTBL, numUpSamplesFldName, records[1]);
		# Find the record and copy some of the fields from the points table to the polygon table
		records[1] = TableKeyFieldLookup(UBasinPolyToPolyTBL, sampleIDFldName, sampleNum$, "Equals");
		TableWriteField(UBasinPolyToPolyTBL, records[1], totalSamplesIncludedFldName, totalUpSamples + 1);
		TableWriteField(UBasinPolyToPolyTBL, records[1], totalSampleBasinsFldName, totalUpSamples);
		TableWriteField(UBasinPolyToPolyTBL, records[1], numUpBasinsFldName, numUpSamples);
		# Start the process of computing the area for the overall sample basin by computing the basins 
		# with no upstream sample points.  Use the area value of the sample basin for the "total" 
		# upstream basin value.  Check if the sample basin has no upstream sample points.
		if (numUpSamples == 0) {
			# Assign the total area, the area of the sample basin
			tempAreaValue = TableReadFieldNum(UBasinPolyToPolyTBL, areaSamplePolysFldName, records[1]);
			TableWriteField(UBasinPolyToPolyTBL, records[1], totalSampleBasinAreaFldName, tempAreaValue);
			numBasinsComputed = numBasinsComputed + 1;
		# Otherwise, set the total number of upstream basins and the area to invalid
		} else {
			TableWriteField(UBasinPolyToPolyTBL, records[1], totalSampleBasinAreaFldName, -1);
		}
	}
	tempCount = 0;
	# Atleast 1 basin has to be the most upstream, so it should always enter the while loop.
	statusC.BarInit(numUserBasins, 0);
	while (numBasinsComputed > 0) {
if (DEBUG) {
	fwritestring(summaryFile, "   numB: " + NumToStr(numBasinsComputed) + "  numS: " + NumToStr(numSamplePoints) + "\n");
}
		# Reset the counter so the while loop will only stop when no new basin values are computed
		tempCount += numBasinsComputed;
		statusC.BarIncrement(numBasinsComputed, 0);
		numBasinsComputed = 0;
		for i = 1 to numSamplePoints {
			# Get the polygon element number for the sample point
			TableReadAttachment(ResultSampleTBL, i, records, "point");
			sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
			records[1] = TableKeyFieldLookup(UBasinPolyToPolyTBL, sampleIDFldName, sampleNum$, "Equals");
			TableGetRecordElementList(UBasinPolyToPolyTBL, records[1], elements, "polygon");
			# Get the current total included area for that basin 
			TableReadAttachment(UBasinPolyToPolyTBL, elements[1], records, "polygon");
			totalInclArea = TableReadFieldNum(UBasinPolyToPolyTBL, totalSampleBasinAreaFldName, records[1]);
			# If the total area is still invalid, we need to try and compute it
			if (totalInclArea == -1) {
				# Add the area for the current polygon into the total
				currTotalArea = UBasinVECT.Poly[elements[1]].POLYSTATS.Area / 1000000;
				# Get the number of upstream samples
				TableReadAttachment(ResultPointToPointTBL, i, records, "point");
				numUpstream = TableReadFieldNum(ResultPointToPointTBL, numUpSamplesFldName, records[1]);
				# Initialize the list and then get the list of upstream sample numbers
				UpSample.Clear();
				UpSample.AddToFront("");
				for j = 1 to numUpstream {
					UpSample.AddToEnd( TableReadFieldStr(ResultPointToPointTBL, upSampleFldName + NumToStr(j), records[1]) );
				}
if (DEBUG) {
	fwritestring(summaryFile, "      sNum: " + sampleNum$ + "  numS: " + NumToStr(numSamplePoints) + "  i:" + NumToStr(i) + "  numU: " + NumToStr(numUpstream) + "\n");
}
				for j = 1 to numUpstream {
					# If the current total is not invalid, keep checking the upstream samples
					if ( currTotalArea != -1) {
						# Get the polygon element that the Sample number refers to and check if it's total area is valid
						records[1] = TableKeyFieldLookup(UBasinPolyToPolyTBL, sampleIDFldName, UpSample[j], "Equals");
						TableGetRecordElementList(UBasinPolyToPolyTBL, records[1], elements, "polygon");
						TableReadAttachment(UBasinPolyToPolyTBL, elements[1], records, "polygon");
						upstreamSamplesTotalArea = TableReadFieldNum(UBasinPolyToPolyTBL, totalSampleBasinAreaFldName, records[1]);
						# If the upstream basin of sample point i has a total basin area value, add it to the current total
						if (upstreamSamplesTotalArea != -1) {
							currTotalArea = currTotalArea + upstreamSamplesTotalArea;
						# Otherwise, the it hasn't been calculated, so set the current total to invalid to stop checking
						} else {
							currTotalArea = -1;
						}
					}
				} # End for j loop
				# If the total included area that was computed is valid, record it to the polygon's table
				if ( currTotalArea != -1 ) {
					# Get the polygon element number for the sample point
					TableReadAttachment(ResultSampleTBL, i, records, "point");
					sampleNum$ = TableReadFieldStr(ResultSampleTBL, sampleIDFldName, records[1]);
					numRecords = TableKeyFieldLookupList(UBasinPolyToPolyTBL, sampleIDFldName, sampleNum$, records, "Equals");
					# Write the new total area to the polygon table
					for j = 1 to numRecords {
						TableWriteField(UBasinPolyToPolyTBL, records[j], totalSampleBasinAreaFldName, currTotalArea);
					}
					# Update the while counter so it will run again
					numBasinsComputed = numBasinsComputed + 1;
if (DEBUG) {
	fwritestring(summaryFile, "         currT: " + NumToStr(currTotalArea) + "  sNum:" + sampleNum$ + "  numR:" + NumToStr(numRecords) + "\n");
}
				}
			} # End if not invalid
		} # End for i loop
	} # End while loop
if (DEBUG) {
	statusC.Message = "Step 17 of 18:  Computing upstream area statistics for each catchment basin (THIRD LOOP)";
	fwritestring(summaryFile, "Step 17 of 18:  Computing upstream area statistics for each catchment basin (THIRD LOOP)" + "\n");
}
	# Copy the Upstream Catchment Basin values to the point and polygon sediment sample databases
	statusC.BarInit(numUserBasins, 0);
	for i = 1 to numUserBasins {
		statusC.BarIncrement(1, 0);
		TableReadAttachment(UBasinPolyToPolyTBL, i, records, "polygon");
		sampleNum$ = TableReadFieldStr(UBasinPolyToPolyTBL, sampleIDFldName, records[1]);
		# Make sure the basin has a sample point
		if (sampleNum$.length != 0) {
			tempAreaValue = TableReadFieldNum(UBasinPolyToPolyTBL, totalSampleBasinAreaFldName, records[1]);
if (DEBUG) {
	fwritestring(summaryFile, "i: " + NumToStr(i) + "  tempA:" + NumToStr(tempAreaValue) + "\n");
}
			# Find the record with the same SampleID and write the total area value to it.
			# The same record may be written two twice if multiple polygons are attached to it, but the value should be the same.
			records[1] = TableKeyFieldLookup(ResultSampleTBL, sampleIDFldName, sampleNum$, "Equals");
			TableWriteField(ResultSampleTBL, records[1], totalSampleBasinAreaFldName, tempAreaValue);
			records[1] = TableKeyFieldLookup(UBasinSampleTBL, sampleIDFldName, sampleNum$, "Equals");
			TableWriteField(UBasinSampleTBL, records[1], totalSampleBasinAreaFldName, tempAreaValue);
		}
	}
	#######################################################################################################
	# Create a user defined upstream flowpath vector (clipped STDFLOWPATH inside USDBASIN polygons)
	statusC.Message = "Step 18 of 18:  Computing the user defined upstream flowpath vector";
	statusC.BarClear(0);
	#######################################################################################################
	if ( mainDLG.GetCtrlByID("id_COMP_USR_UP_FLOWPATH").GetValueNum()) {
		class VECTOR USDUpFlowVECT;
		CreateVector(USDUpFlowVECT, OutputProjectFile, "USD_UPFLOWPATH", "Upstream flowpath from sample points");
		USDUpFlowVECT = VectorExtract(UBasinVECT, STDFlowPath, "InsideClip", "None", "1", "1", "1");
		CloseVector(USDUpFlowVECT);
	}
	#######################################################################################################
	# End watershed main process
	#######################################################################################################
	CloseRaster(SampleDEM);
	CloseVector(SampleVECT);
	CloseVector(WarpVECT);
	CloseVector(ResultVECT);
	CloseVector(STDFlowPath);
	CloseVector(UBasinVECT);
} 
######################################################################################################
# Dialog Related Functions
######################################################################################################
######################################################################################################
# Called when "Run..." is pressed. 
proc OnRunPressed( ) {
	mainDLG.GetCtrlByID("id_RUN_BUTTON").SetEnabled(0);
	if (sampleTblName == "" or sampleIDFldName	== "") {
		message = "The unique sample ID table and field values are not selected.\n";
		message += "Please reselect the input vector and select the unique table and field";
		PopupMessage(message);
		mainDLG.GetCtrlByID("id_RUN_BUTTON").SetEnabled(1);
		print(message);
		fwritestring(summaryFile, message);
		return;
	}
	# Set up the summary file
	summaryFile = fopen(OutputSummaryFile, "w");
	# Call Starting Procedure Here!!!!!
	statusD.Create();
	statusC = statusD.CreateContext();
	string test = SampleIDThatIsNotUnique();
	if ( test != "" ) {
		InputVECTORfilename = InputVECTORobjectName = sampleTblName = sampleIDFldName = "";
		mainDLG.GetCtrlByID("id_INPUT_VECTOR").SetValueStr("");
		mainDLG.GetCtrlByID("id_TABLE_NAME").SetValueStr("");
		mainDLG.GetCtrlByID("id_FIELD_NAME").SetValueStr("");
		statusD.destroy();
		message = "The following sample ID in the selected table is not unique:  " + test + "\n";
		message += "Please resolve the situation in the table before running the script again, \n";
		message += "or select a new table and field by reselecting the input vector.";
		PopupMessage(message);
		mainDLG.GetCtrlByID("id_RUN_BUTTON").SetEnabled(1);
		return;
	}
	# Set up a timer to record completion time
	local class TIMER time;
	time.Running = 0;
	time.Value = 0;
	time.Running = 1;
	message = "Starting Watershed Process...\n\n";
	print(message);
	fwritestring(summaryFile, message);
	StartWatershedProcess();
	statusD.Destroy();
	time.Running = 0;
	message = sprintf("Time to complete: %2d minutes %0.2d seconds\n", floor(time.Value / 60), floor(time.Value % 60));
	PopupMessage(message);
	print(message);
	fwritestring(summaryFile, message);
	mainDLG.Close(1);
}
######################################################################################################
# Called when "Canel" is pressed.  Closes dialog and exits script.
proc OnCancelPressed( ) {
	mainDLG.Close(1);
}
######################################################################################################
# Called when "Input DEM..." is pressed.  Opens dialog to select raster.
proc OnGetInputDEMPressed( ) {
	local class RASTER DEM;
	# Get the raster and set global variables
	GetInputRaster(DEM);
	class RVC_OBJITEM DEMobjitem = DEM.GetObjItem();
	InputDEMfilename = DEMobjitem.GetFilePath();
	InputDEMobjectName = DEMobjitem.GetObjectPath();
	CloseRaster(DEM);
	class FILEPATH demFilepath(InputDEMfilename);
	# Update the dialog
	local class GUI_CTRL_EDIT_STRING text = mainDLG.GetCtrlByID("id_INPUT_DEM");
	text.SetValue(InputDEMfilename + " / " + InputDEMobjectName, 0);
	mainDLG.GetCtrlByID("id_INPUT_VECTOR_BUTTON").SetEnabled(1);
}
######################################################################################################
# Called when "Input Vector" is pressed.  Opens dialog to select vector.
proc OnGetInputVECTORPressed( ) {
	local class VECTOR Vect;
	# Get the Vector and set global variables
	GetInputVector(Vect);
	InputVECTORfilename = Vect.$Info.Filename;
	InputVECTORobjectName = Vect.$Info.Name;
	# Update the dialog
	local class GUI_CTRL_EDIT_STRING text = mainDLG.GetCtrlByID("id_INPUT_VECTOR");
	text.SetValue(InputVECTORfilename + " / " + InputVECTORobjectName, 0);
	mainDLG.GetCtrlByID("id_OUTPUT_PROJECT_FILE_BUTTON").SetEnabled(1);
	PopupSelectTableField(OpenVectorPointDatabase(Vect), sampleTblName, sampleIDFldName);
	mainDLG.GetCtrlByID("id_TABLE_NAME").SetValueStr(sampleTblName);
	mainDLG.GetCtrlByID("id_FIELD_NAME").SetValueStr(sampleIDFldName);
	CloseVector(Vect);
}
######################################################################################################
# Called when the "Use Mask" toggle is pressed 
proc OnUseMaskToggleChanged() {
	numeric state = mainDLG.GetCtrlByID("id_MASK_TOGGLE").GetValueNum();
	mainDLG.GetCtrlByID("id_MASK_BUTTON").SetEnabled(state);
	mainDLG.GetCtrlByID("id_INPUT_MASK").SetEnabled(state);
}
######################################################################################################
# Called when "Mask..." is pressed.  
proc OnMaskButtonPressed() {
	local class RASTER Mask;
	# Get the raster and set global variables
	GetInputRaster(Mask);
	InputMaskfilename = Mask.$Info.Filename;
	InputMaskobjectName = Mask.$Info.Name;
	CloseRaster(Mask);
	# Update the dialog
	local class GUI_CTRL_EDIT_STRING text = mainDLG.GetCtrlByID("id_INPUT_MASK");
	text.SetValue(InputMaskfilename + " / " + InputMaskobjectName, 0);
}
######################################################################################################
# Called when "Restore Defaults" is pressed.  
proc OnRestoreDefaultsPressed() {
	mainDLG.GetCtrlByID("id_OUTLET").SetValueNum(256);
	mainDLG.GetCtrlByID("id_BRANCH").SetValueNum(128);
	mainDLG.GetCtrlByID("id_INLET").SetValueNum(32);
	mainDLG.GetCtrlByID("id_BASIN").SetValueNum(378);
}
######################################################################################################
# Called when the "Snap to closest flowpath" toggle button is pressed
proc OnSnapLineToggleChanged() {
	local class GUI_CTRL_TOGGLEBUTTON toggle = mainDLG.GetCtrlByID("id_SNAP_LINE_TOGGLE");
	mainDLG.GetCtrlByID("id_SNAP_LINE_LABEL").SetEnabled(toggle.GetValue());
	mainDLG.GetCtrlByID("id_SNAP_LINE_MAX_DIST").SetEnabled(toggle.GetValue());
}
######################################################################################################
# Called when "Snap to downstream node" toggle button is pressed
proc OnSnapNodeToggleChanged() {
	local class GUI_CTRL_TOGGLEBUTTON toggle = mainDLG.GetCtrlByID("id_SNAP_NODE_TOGGLE");
	mainDLG.GetCtrlByID("id_SNAP_NODE_LABEL").SetEnabled(toggle.GetValue());
	mainDLG.GetCtrlByID("id_SNAP_NODE_MAX_DIST").SetEnabled(toggle.GetValue());
}
######################################################################################################
# Called when "Output Project File..." is pressed.  
proc OnGetOutputProjectFilePressed( ) {
	local class GUI_CTRL_EDIT_STRING text = mainDLG.GetCtrlByID("id_OUTPUT_PROJECT_FILE");
	local class STRING tempStartDir = _context.ScriptDir;
	if (InputDEMfilename != "") {
		demFilepath.StripToFolder();
		tempStartDir = demFilepath;
	}
	OutputProjectFile = GetOutputFileName(tempStartDir, "Select project file to save files to:", "rvc");
	class FILEPATH outputFilepath(OutputProjectFile);
	# Check if the user pressed "Cancel" in the dialog
	if (OutputProjectFile == tempStartDir) {
		text.ClearValue(0);
		text.SetEnabled(0);
		return;
	} 
	# Delete the file if it exists.  The user would have received a "Overwrite this file?" warning.
	if (fexists(OutputProjectFile)) {
		DeleteFile(OutputProjectFile);
	}
	CreateProjectFile(OutputProjectFile, "Created by SML Watershed Script to save resulting files");
	text.SetValue(OutputProjectFile, 0);
	mainDLG.GetCtrlByID("id_OUTPUT_SUMMARY_FILE_BUTTON").SetEnabled(1);
} 
######################################################################################################
# Called when the "Get Summary File..." button is pressed.
proc OnGetOutputSummaryFilePressed() {
	local class GUI_CTRL_EDIT_STRING text = mainDLG.GetCtrlByID("id_OUTPUT_SUMMARY_FILE");
	outputFilepath.StripToFolder();
	local class STRING defaultName$ = outputFilepath + "/Summary";
	OutputSummaryFile = GetOutputFileName(defaultName$, "Select output text file", "txt");
	text.SetValue(OutputSummaryFile, 0);
	mainDLG.GetCtrlByID("id_RUN_BUTTON").SetEnabled(1);
}
######################################################################################################
# Procedure called to set up the dialog using the XML code specified in this script.  Dialog is made
# using a Modal function call.  When the dialog closes, the script will end.
proc OnCreateDialog( ) {
	class XMLDOC mainDOC;
	class XMLNODE mainNODE;
	errXML = mainDOC.Parse(xmlCODE$);
	if ( errXML < 0 ) {
		PopupError( errXML );	# pop up an error dialog
		Exit( );
	}
	mainNODE = mainDOC.GetElementByID( "MAIN_DIALOG" );
	if ( mainNODE == 0 ) {
		PopupMessage( "Could not find main dialog node in XML document" );
		Exit();
	}
	mainDLG.SetXMLNode(mainNODE);
	errDLG = mainDLG.DoModal();
	if ( errDLG == -1 ) {
		Exit();
	}
}
######################################################################################################