home products news downloads documentation support gallery online maps resellers search
TNTmips Downloads Menu

HOME

CONTACT US

CURRENT RELEASE
  TNT 2013

DEVELOPMENT VERSION
  TNT 2014

TNTmips Pro
PRIOR RELEASES
  TNT 2012

FREE SOFTWARE
  TNTmips Free
  TNTatlas
  TNTsdk

MORE DOWNLOADS
  HASP Key Driver
  Screen Recorder
  TNT Language Kits
  Sample Geodata
  TNT Scripts

DOCUMENTATION
  TNTmips Tutorials
  Tutorial Datasets
  Technical Guides
  Scripts
  Quick Guides

MORE INFO
  Download FAQs
  FTP
  Download Managers
  Find Reseller

SITE MAP


SampleCatchments.sml


######################################################################################################
#
# 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$ = '

	
		
			
				
					
					
				
				
					
					
						
						
							
							
							
							
						
					
				
			
		
		
';

######################################################################################################
######################################################################################################


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


















Back Home ©MicroImages, Inc. 2013 Published in the United States of America
11th Floor - Sharp Tower, 206 South 13th Street, Lincoln NE 68508-2010   USA
Business & Sales: (402)477-9554  Support: (402)477-9562  Fax: (402)477-9559
Business info@microimages.com  Support support@microimages.com  Web webmaster@microimages.com

25 March 2009

page update: 26 May 11