SRTMfill.sml

  Download

More scripts: Advanced

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
######################################################################################################
#
# SRTMfill.sml ("Stand Alone" Script)
#
######################################################################################################
#
# Authors:
#   Dave Breitwisch, MicroImages, Inc
#
# Started: 07 April 2005
# Updated: 28 March 2006
#
# Requires: TNTmips2005:71 or later
#
numeric version = round(_context.Version * 10);
if (version < 71) {
	PopupMessage("This version requires that you are using TNTmips2005:71 or later");
	Exit();
}
#
######################################################################################################
#
# Description of Script:
#
#   This is a "Stand Alone" script whose purpose is to fill in null cell values in the released data  
#   from the Shuttle Radar Topography Mission flown in February of 2000.  
#   This current version of the script: 
#        fills single cells by interpolation,
#        fills null voids that are bounded by a constant value, and 
#        fills voids using elevation data from reference DEM rasters provided by the user.
#   
#
#   Void Filling Procedures:
#
#     Single Cell Interpolation - The user can specify the number of surrounding cells that are
#        required (minimum = 6) to be present for the single cell to be filled.  If the single null 
#        cell passes this test, the average of all the surrounding non-null cells is used.  This procedure 
#        is called multiple times until there are no remaining single null cells that pass the test.
#
#     Constant Boundary Filling - The algorithm used for this procedure searches for null voids that
#        have a constant value around their edges.  When a null void is found, the algorithm 
#        traverses around the boundary of the void.  If all the surrounding values are the same, 
#        the algorithm fills in it's traversed path with that constant value.  The remaining cells
#        inside of this path are filled in when the algorithm detects this new null void.  This
#        is especially useful for filling in voids within a water surface.
#        NOTE:  If a null void covers a real area of different elevation, but the surrouding cells
#               are the same value, the void is filled in with the constant value.  This is the
#               case when an SRTM void includes an island within a horizontal water body.
#
#     Reference DEM Filling - This allows the user to use multiple reference DEMs, assumed to be 
#        accurate and properly georeferenced, to fill the null voids.  The DEM data is assumed to be
#        in meters unless the proper cell value scale is set for the raster.  Additional options such
#        as a maximum elevation difference and blending are available.
#
#        Maximum Elevation Difference - For each reference DEM, the values of the SRTM cells are 
#           compared to the matching georeferenced values of the DEM.  If the differnece between the
#           values is greater then specified by the user, the SRTM cell is replaced by null and will
#           eventually be filled in with the reference DEM value. This is done under the assumption
#           that the reference DEMs are more accurate.
#
#        Blending - This procedure uses the value specified by the user to blend the areas around
#           the null voids filled in by the reference DEMs.  The value represents the distance away
#           from the boundary of the null void (in number of cells) over which to blend.  Blending
#           is performed using nested 1-cell-wide buffer zones.  The final SRTM cell values in each 
#           buffer zone are linear weighted averages of the original SRTM cell value and the reference 
#           DEM cell value with the weighting factors determined by the number of buffer zones and
#           the position of the current buffer zone in the nesting sequence.  
#           
#           Example:  blending over 4 nested buffer zones around each void
#              Zone 1 : cell = SRTM value + 4/5 * diffence between the values (Boundary of void)
#              Zone 2 : cell = SRTM value + 3/5 * diffence between the values
#              Zone 3 : cell = SRTM value + 2/5 * diffence between the values
#              Zone 4 : cell = SRTM value + 1/5 * diffence between the values (Furthest from void)
#
#           An additional option is given to the user to save the raster and vector objects created 
#           by this filling process.  These can be used for data verification.  These objects are made
#           after the Single Cell Interpolation and Constant Boundry Filling operations are done, if
#           chosen by the user.  The following objects are created:
#              BinNullVoids (Binary Raster): 1 = Matching cell to be filled in by reference DEM value
#              BufferR# (Binary Raster): 1 = Matching cell is in Zone number #
#              NullVoids (Vector): Shows boundaries of BinNullVoids Raster
#              BufferV# (Vector): Shows outer boundaries of the matching BufferR# Raster
#
######################################################################################################
######################################################################################################
# Global Variable Declarations
######################################################################################################
class XMLDOC mainDOC, progressDOC;
class XMLNODE mainNODE, progressNODE;
class GUI_DLG mainDLG, progressDLG;
numeric errXML, errDLG;
string mainxmlCODE$, progressxmlCODE$;
string InputRasterFilename$, InputRasterObjectName$;
string OutputRasterFilename$, OutputRasterObjectName$;
string LogFilename$;
class STRINGLIST RefDEMFilenames$, RefDEMObjectNames$;
class RASTER inR;
class RASTER outR;
numeric rows, cols;
string initType;
numeric numValidNulls;
class RASTER RefR;
numeric RefDataScale;
class RASTER binVoidR;
class VECTOR nullVoidsV;
array numeric element[1,1];
array numeric buffinodes[1];
###########################
# User Selected Values
###########################
numeric cellBufferDist;
numeric threshold;
numeric numRequiredAround;
numeric numReferenceDEMs;
string TempSRTMfile;
######################################################################################################
######################################################################################################
# Dialog specification in XML
######################################################################################################
mainxmlCODE$ = '<?xml version="1.0"?>
<root>
	<dialog id = "MAIN_DIALOG" title="SRTM Void Filling Process" Buttons = "">
		<label>Select input and output SRTM Rasters:</label>
		<pane Orientation = "Horizontal">
			<pushbutton Name = "Input SRTM Raster... " OnPressed = "OnGetInputSRTMPressed();"/>
			<edittext id = "id_INPUT_SRTM_RASTER" Width = "70" ReadOnly = "True"/>
		</pane>
		<pane Orientation = "Horizontal">
			<pushbutton id = "id_GET_OUTPUT_SRTM_BUTTON" Name = "Output SRTM Raster..." Enabled = "0" OnPressed = "OnGetOutputSRTMPressed();" />
			<edittext id = "id_OUTPUT_SRTM_RASTER" Width = "70" ReadOnly = "True"/>
		</pane>
		<groupbox Name = "Compute Fill from Input SRTM:">
			<pane Orientation = "Horizontal">
				<pane Orientation = "Vertical" HorizResize = "Expand">
					<togglebutton id = "id_FILL_SINGLE_NULLS_TOGGLE" Name = "Interpolate single null cells" Selected = "0" OnChanged = "OnUseSingleCellToggleChanged();"/>
					<togglebutton id = "id_FILL_CONSTANT_NULL_VOIDS_TOGGLE" Name = "Fill null voids surrounded by constant value (Ex: Water)" Selected = "0"/>
				</pane>
				<pane Orientation = "Vertical" HorizResize = "Fixed">
					<pane Orientation = "Horizontal" VertResize = "Fixed">
						<label id = "id_NUM_REQUIRED_SURROUNDING_LABEL" HorizAlign = "Right" Enabled = "0">Number of surrounding cells required (6-8):</label>
						<editnumber id = "id_NUM_REQUIRED_SURROUNDING" Width = "3" HorizResize = "Fixed" Default = "8" MinVal = "1" MaxVal = "8" Precision = "0" Enabled = "0"/>
					</pane>
				</pane>
			</pane>
		</groupbox>
		<groupbox Name = "Fill from Reference DEM:">
			<pane Orientation = "Horizontal">
				<pane Orientation = "Vertical" HorizResize = "Expand">
					<togglebutton id = "id_FILL_VOIDS_WITH_REF_DEM_TOGGLE" Name = "Fill null voids using reference DEM rasters" Selected = "0" OnChanged = "OnUseRefDemToggleChanged();"/>
				</pane>
				<pane Orientation = "Vertical" HorizResize = "Fixed">
					<pane Orientation = "Horizontal" VertResize = "Fixed">
						<label id = "id_NUM_REF_DEM_LABEL" HorizAlign = "Right" Enabled = "0">Number of reference DEMs:</label>
						<editnumber id = "id_NUM_REF_DEM" Width = "3" HorizResize = "Fixed" MinVal = "1" Precision = "0" OnChanged = "OnNumDEMChanged();" Enabled = "0"/>
						<pushbutton id = "id_GET_REF_DEM_BUTTON" Name = "Get DEMs..." Enabled = "0" OnPressed = "OnGetRefDEMRastersPressed();"/>
					</pane>
				</pane>
			</pane>
			<groupbox Name = "Reference DEMs are processed in the order selected (top to bottom):" HorizResize = "Fixed" HorizAlign = "Center">
				<listbox id = "id_REF_DEM_LIST" Width = "99" Height = "5" Enabled = "1"/>
			</groupbox>
			<pane Orientation = "Horizontal">
				<pane Orientation = "Vertical" HorizResize = "Expand">
					<togglebutton id = "id_USE_THRESHOLD_TOGGLE" Name = "Replace values that violate the max. elevation difference" Selected = "0" Enabled = "0" OnChanged = "OnUseThresholdToggleChanged();"/>
					<togglebutton id = "id_USE_BUFFER_ZONE_TOGGLE" Name = "Use buffer zones to feather around null voids" Selected = "0" Enabled = "0" OnChanged = "OnUseBufferZoneToggleChanged();"/>
				</pane>
				<pane Orientation = "Vertical" HorizResize = "Fixed">
					<pane Orientation = "Horizontal" VertResize = "Fixed">
						<label id = "id_THRESHOLD_VALUE_LABEL" HorizAlign = "Right" Enabled = "0">Max. elevation difference (meters):</label>
						<editnumber id = "id_THRESHOLD_VALUE" Width = "6" HorizResize = "Fixed" MinVal = "1" Default = "100" Precision = "2" Enabled = "0"/>
					</pane>
					<pane Orientation = "Horizontal" VertResize = "Fixed">
						<label id = "id_NUM_BUFFER_ZONE_LABEL" HorizAlign = "Right" Enabled = "0">Number of Buffer Zones (1 cell wide):</label>
						<editnumber id = "id_NUM_BUFFER_ZONE" Width = "6" HorizResize = "Fixed" MinVal = "1" Default = "3" Precision = "0" Enabled = "0"/>
					</pane>
				</pane>
			</pane>
		</groupbox>
		<pane Orientation = "Horizontal">
			<pane Orientation = "Vertical" HorizResize = "Fixed">
				<togglebutton id = "id_SAVE_TEMP_FILE_TOGGLE" Name = "Save temp files" Selected = "0" OnChanged = "OnSaveTempFileToggleChanged();"/>
			</pane>
			<pane Orientation = "Veftical" HorizResize = "Expand">
				<pane Orientation = "Horizontal">
					<pushbutton id = "id_SAVE_TEMP_FILE_BUTTON" Name = "Project file..." HorizResize = "Fixed" Enabled = "0" OnPressed = "OnGetTempFilePressed();"/>
					<edittext id = "id_TEMP_FILE" HorizResize = "Expand" Enabled = "0" ReadOnly = "True"/>
				</pane>
			</pane>
		</pane>
		<pane Orientation = "Horizontal" HorizResize = "Expand">
			<pane Orientation = "Vertical"/>
			<pushbutton id = "id_RUN_BUTTON" Name = "Run..." HorizResize = "Fixed" Enabled = "0" OnPressed = "OnRunPressed();"/>
			<pushbutton id = "id_CANCEL_BUTTON" Name = "Cancel" HorizResize = "Fixed" Enabled = "1" OnPressed = "OnCancelPressed();"/>
		</pane>
	</dialog>
</root>';
######################################################################################################
######################################################################################################
# Dialog specification in XML
######################################################################################################
progressxmlCODE$ = '<?xml version="1.0"?>
<root>
	<dialog id = "VERIFY_DIALOG" title="Progress Panel" Buttons = "" OnOpen="OnVerifyOpen();">
		<label>Checking for null cells...</label>
	</dialog>
	<dialog id = "PROGRESS_DIALOG" title="Progress Panel" Buttons = "" OnOpen="OnProgressOpen();">
		<label>Filling null cells...</label>
	</dialog>
</root>';
######################################################################################################
######################################################################################################
# Start of the Script
######################################################################################################
clear();
proc OnCreateDialog(); #Function Prototype
OnCreateDialog();
######################################################################################################
######################################################################################################
#	Script Functions and Procedures
######################################################################################################
######################################################################################################
# Returns the number of NULL cells that are in the raster
func NumNullCellsInRaster(class RASTER rast) {
	local numeric i, j, nullCount = 0;
	for i = 1 to NumLins(rast) {
		for j = 1 to NumCols(rast) {
			if (IsNull(rast[i,j])) {
				nullCount++;
			}
		}
	}
	return nullCount;
}
######################################################################################################
# Return the number of cells surrounding the location of the cell passed that are null.
func NumNullsAround(numeric i, numeric j) {
	local numeric count = 0;
	# Add 1 to count if specified neighboring cell is a valid Raster cell and is Null
	if ((i != 1)    && (j != cols) && IsNull(outR[i-1,j+1]))  count++;
	if ((i != 1)                   && IsNull(outR[i-1,j  ]))  count++;
	if ((i != 1)    && (j != 1)    && IsNull(outR[i-1,j-1]))  count++;
	if (               (j != cols) && IsNull(outR[i  ,j+1]))  count++;
	if (               (j != 1)    && IsNull(outR[i  ,j-1]))  count++;
	if ((i != rows) && (j != cols) && IsNull(outR[i+1,j+1]))  count++;
	if ((i != rows)                && IsNull(outR[i+1,j  ]))  count++;
	if ((i != rows) && (j != 1)    && IsNull(outR[i+1,j-1]))  count++;
	return count;
}
######################################################################################################
# Returns the average value of all surrounding cells that are not null
func InterpCellValue(numeric i, numeric j) {
	local numeric sumvalue = 0;
	local numeric numCells = 0;
    
	# Check if the neighboring cell is valid and not null.  If ok, add to sum and increment the count 
	if ((i != 1) && (j != cols) && (!IsNull(outR[i-1,j+1]))) {
		sumvalue += outR[i-1,j+1];
		numCells++;  
	} 
	if ((i != 1) && (!IsNull(outR[i-1,j]))) {
		sumvalue += outR[i-1,j];
		numCells++;  
	}
	if ((i != 1) && (j != 1) && (!IsNull(outR[i-1,j-1]))) {
		sumvalue += outR[i-1,j-1];
		numCells++;  
	}
	if ((j != cols) && (!IsNull(outR[i,j+1]))) {
		sumvalue += outR[i,j+1];
		numCells++;  
	}
	if ((j != 1) && (!IsNull(outR[i,j-1]))) {
		sumvalue += outR[i,j-1];
		numCells++;  
	}
	if ((i != rows) && (j != cols) && (!IsNull(outR[i+1,j+1]))) {
		sumvalue += outR[i+1,j+1];
		numCells++;  
	}
	if ((i != rows) && (!IsNull(outR[i+1,j]))) {
		sumvalue += outR[i+1,j];
		numCells++;  
	}
	if ((i != rows) && (j != 1) && (!IsNull(outR[i+1,j-1]))) {
		sumvalue += outR[i+1,j-1];
		numCells++;
	}
  
	# Return the average of the sum
	return sumvalue/numCells;
}
######################################################################################################
# This process traverses through the SRTM raster and looks for single null cells that have less then
# the allowed number of nulls surrounding them.  For these cells the function interpolates a value 
# based on the average of the surrounding real cell values.
func CheckNegligibleNulls( numeric allowedNulls ) {
	local numeric cellsChanged = 0;
	SetStatusMessage("Interpolating Negligible Null Cells");
	local numeric i, j;
	# Traverse through the raster
	for i = 1 to rows {
		SetStatusBar(i, rows);
		for j = 1 to cols {
			# Check if the cell is null
			if (IsNull(outR[i, j])) {
				# Check if it has less then the number of surrounding null cells allowed
				if ( NumNullsAround(i, j) <= allowedNulls ) {
					# Interpolate the cell value and update the report counter
					outR[i, j] = InterpCellValue(i, j);
					cellsChanged++;
				}          
			}
		} 
	} 
	print("   Number of single null cells changed in this pass: " + NumToStr(cellsChanged) + "\n");
	return cellsChanged;
}
######################################################################################################
# This procedure creates a binary raster that symbolizes the overlap of the input SRTM raster and the
# reference DEM.  The value of 1 indicates that the SRTM has a null cell.  A vector is also created
# that contains the boundaries of the null cells (value of 1) in the binary raster.
proc CreateNullRasterVector( ) {
	print("Creating Void Mask and Void Boundries\n");
	CreateRaster(binVoidR, TempSRTMfile, "BinNullVoids", "Binary Raster showing Null Voids", rows, cols, "binary");
	CopySubobjects(outR, binVoidR, "GEOREF");
	SetNull(binVoidR, 0);
	# Set the cell to 1 if the matching SRTM raster cell is null, else set it to 0.
	local numeric i, j, I = 1, J = 1;
	for i = 1 to rows {
		for j = 1 to cols {
			# Offset the line and column counters to match the two rasters
			if ( IsNull(outR[i, j]) ) {
				binVoidR[I,J] = 1;
			} else {
				binVoidR[I,J] = 0;
			}
			J++;
		}
		I++;
		J = 1;
	}
	# Create the vector with boundaries around the 1's in the binary raster
	CreateVector(nullVoidsV, TempSRTMfile, "NullVoids", "Boundary of Null Voids (after single cell pass)");
	nullVoidsV = RasterToVectorBound(binVoidR, "DoImpliedGeoref");
	# Save the object numbers so they can be reopened later
	ResizeArrayClear(buffinodes, (cellBufferDist+1)*2);
	buffinodes[1] = GetObjectNumber(binVoidR);
	buffinodes[2] = GetObjectNumber(nullVoidsV);
	CloseRaster(binVoidR);
	CloseVector(nullVoidsV);
}
######################################################################################################
# This procedure traverses through the SRTM raster and replaces any null values with the corresponding
# cell value from the reference DEM.  The SRTM cell will remain null if the current reference DEM does  
# not overlap or if the corresponding reference DEM cell is null.
proc FillWithRefRaster() {
	print("Filling Voids with Reference Data\n");
	local class GEOREF outG, refG;
	local class POINT2D point;
	outG = GetLastUsedGeorefObject(outR);
	refG = GetLastUsedGeorefObject(RefR);
	# create a transparm if the CRS's are different
	local numeric same = 1;
	if (outG.CoordRefSys.Name != refG.CoordRefSys.Name) {
		same = 0;
		local class TRANSPARM trans;
		trans.InputCoordRefSys = outG.CoordRefSys;
		trans.OutputCoordRefSys = refG.CoordRefSys;
	}
	SetStatusMessage("Filling holes with Reference Data");
	# traverse through the overlapping area
	local numeric i, j;
	for i = 1 to rows {
		SetStatusBar(i, rows);
		for j = 1 to cols {
			# If the cell is null, find the matching cell in the reference DEM
			if ( IsNull(outR[i,j]) ) {
				point = ObjectToMap(outR, j, i, outG);
				# Convert to the correct CRS if they are not the same
				if (!same) {
					point = trans.ConvertPoint2DFwd(point);
				}
				# convert map coordinates to SRTM raster line/column
				point = MapToObject(refG, point.x, point.y, RefR);
				point.x = round(point.x);
				point.y = round(point.y);
				# Check to make sure the point is valid, then replace it
				if ( point.x >= 1 and point.x <= RefR.$Info.NumCols) {
					if ( (point.y >= 1) && (point.y <= RefR.$Info.NumLins) ) {
						outR[i,j] = RefR[point.y, point.x] * RefDataScale;
					}
				}
 			}
		}
	}
}
######################################################################################################
# This procedure blends the SRTM data with the Reference data that was used to fill the null cells
# based on the distance from the null voids that the user specifies.
proc BlendBufferZone( class RASTER zoneBufferR, numeric zoneNum ) {
	print("   Buffering in Zone " + NumToStr(zoneNum) + " of " + NumToStr(cellBufferDist) + "\n");
	local class POINT2D point;
	local class GEOREF outG, refG;
	local numeric I = 1, J = 1;
	outG = GetLastUsedGeorefObject(outR);
	refG = GetLastUsedGeorefObject(RefR);
	# create a transparm if the CRS's are different
	local numeric same = 1;
	if (outG.CoordRefSys.Name != refG.CoordRefSys.Name) {
		same = 0;
		local class TRANSPARM trans;
		trans.InputCoordRefSys = outG.CoordRefSys;
		trans.OutputCoordRefSys = refG.CoordRefSys;
	}
	SetStatusMessage("Computing new values in Buffer Zone");
	# traverse through the overlapping area
	local numeric m, n, buffFactor;
	for m = 1 to rows {
		SetStatusBar(m, rows);
		for n = 1 to cols {
			# Start the blending process if the cell is in the buffer zone
			if ( zoneBufferR[I, J] == 1 ) {
				point = ObjectToMap(outR, n, m, outG);
				# Convert point to the correct CRS if not the same
				if (!same) {
					point = trans.ConvertPoint2DFwd(point);
				}
				point = MapToObject(refG, point.x, point.y, RefR);
				point.x = round(point.x);
				point.y = round(point.y);
				# Make sure the matching cell in the DEM is valid
				if ( point.x >= 1 and point.x <= RefR.$Info.NumCols) {
					if ( (point.y >= 1) && (point.y <= RefR.$Info.NumLins) ) {
						# Assign the output cell the value of the SRTM plus the linear differnce based
						# on what zone the cells are in.
						buffFactor = (cellBufferDist - zoneNum + 1) * 1.0 / (cellBufferDist * 1.0 + 1);
						outR[m,n] = outR[m,n] + (buffFactor) * ((RefR[point.y, point.x] * RefDataScale) - outR[m,n]);
					}
				}
			}
			J++;
		}
		I++;
		J = 1;
	}
}
######################################################################################################
# This procedure uses the binVoidR raster created early along with the morphological functions to grow
# the voids larger based on the zone number.  A boundary vector is created with each new raster.
proc CreateVectRastBufferZoneInit(numeric zone) {
	SetStatusMessage("Computing initial void Vector and Raster");
	local string zone$ = NumToStr(zone);
	print("   Growing Buffer Raster " + zone$ + " of " + NumToStr(cellBufferDist) + "\n");
	# Create a raster the size of binVoidR and dilate the null void based on the zone value
	local class RASTER buffR;
	local numeric tempNumLins = NumLins(binVoidR);
	local numeric tempNumCols = NumCols(binVoidR);
	CreateRaster(buffR, TempSRTMfile, "BufferR"+zone$, "Buffer Zone "+zone$, tempNumLins, tempNumCols, "binary");
	CopySubobjects(binVoidR, buffR, "GEOREF");
	SetNull(buffR, 0);
	MorphDilation(binVoidR, buffR, element, (zone*2)+1, (zone*2)+1);
	# Create a vector that bounds each of the null voids
	local class VECTOR buffV;
	CreateVector(buffV, TempSRTMfile, "BufferV"+zone$, "Boundary of Buffer Zone "+zone$);
	buffV = RasterToVectorBound(buffR, "DoImpliedGeoref");
	# Save the inodes for future use
	buffinodes[(zone*2)+1] = GetObjectNumber(buffR);
	buffinodes[(zone*2)+2] = GetObjectNumber(buffV);
	CloseVector(buffV);
	CloseRaster(buffR);
} 
######################################################################################################
# This procedure compares each of the binary rasters with the raster that is "grown 1 size smaller" so
# the resulting raster will only contain the zone boundaries.  All else will be 0's.
proc ComputeBufferZoneRasters( numeric zone ) {
	print("   Creating Buffer Zone Raster " + NumToStr(zone) + " of " + NumToStr(cellBufferDist) + "\n");
	# Open the working raster and the "1 less grown" raster
	local string name;
	local class RASTER grownR;
	local class RASTER prevR;
	name = GetObjectName(TempSRTMfile, buffinodes[(zone*2)+1]);
	OpenRaster(grownR, TempSRTMfile, name);
	name = GetObjectName(TempSRTMfile, buffinodes[((zone-1)*2)+1]);
	OpenRaster(prevR, TempSRTMfile, name);
	local numeric tempNumLins = NumLins(grownR);
	local numeric tempNumCols = NumCols(grownR);
	SetStatusMessage("Computing Buffer Zone Raster");
	# Traverse through the working raster
	local numeric r, c;
	for r = 1 to tempNumLins {#rows {
		SetStatusBar(r, tempNumLins);
		for c = 1 to tempNumCols {#cols {
			# Do and XOR with the two rasters
			if ( (IsNull(prevR[r,c])) && (grownR[r,c] == 1) ) { 
				grownR[r,c] = 1;
			} else {
				grownR[r,c] = 0;
			}
		}
	}
	CloseRaster(grownR);
	if ( zone != 1 ) {    # Do not close binVoidR
		CloseRaster(prevR);
	}
}
######################################################################################################
# This procedure controls the process of blending the reference raster data that was used to fill the 
# null voids in the SRTM data.
proc BlendWithRefRaster() {
	# Create an element large enough to satisfy the users number of blending zones.  This is used in 
	# the morphological dilation procedures
	ResizeArrayClear(element, (cellBufferDist*2)+1, (cellBufferDist*2)+1);
	numeric k, i;
	for k = 1 to (cellBufferDist*2)+1 {
		for i = 1 to (cellBufferDist*2)+1 {
			element[k,i] = 1;
		}
	}
	# Create the initial binary rastes and grow them according the the zone value
	print("Using Raster Dilation to produce buffer Rasters \n");
	for k = 1 to cellBufferDist {
		CreateVectRastBufferZoneInit(k);
	}
	# Do an XOR with each of the raster with the "1 lesser grown" raster.  Produces binary zones.
	print("Using buffer Rasters to create Buffer Zones \n");
	for k = cellBufferDist to 1 step -1 {
		ComputeBufferZoneRasters(k);
	}
	print("Buffering around Voids to match data\n");
	# Call the procedure that will blend the reference DEM and SRTM data using the zone rasters
	local class RASTER blendR;
	local string name;
	for k = 1 to cellBufferDist {
		name = GetObjectName(TempSRTMfile, buffinodes[(k*2)+1]);
		OpenRaster(blendR, TempSRTMfile, name);
		BlendBufferZone(blendR, k);
		CloseRaster(blendR);
	}
}
######################################################################################################
# This procedure scans through the overlapping SRTM and reference DEM rasters and removes the SRTM 
# value if the elevation difference is greater then the threshold specified by the user.  It is then
# replaced with the reference DEM data in a later procedure.
proc RemoveThresholdViolations( ) {
	local class POINT2D point;
	local class GEOREF outG, refG;
	print("Removing Threshold Violations to Referance Raster \n");
	outG = GetLastUsedGeorefObject(outR);
	refG = GetLastUsedGeorefObject(RefR);
	# Create a transparm if the CRSs are not the same
	local numeric same = 1;
	if (outG.CoordRefSys.Name != refG.CoordRefSys.Name) {
		same = 0;
		local class TRANSPARM trans;
		trans.InputCoordRefSys = outG.CoordRefSys;
		trans.OutputCoordRefSys = refG.CoordRefSys;
	}
	SetStatusMessage("Removing values that violate the specified Threshold to the reference data\n");
	# Traverese the SRTM raster
	local numeric numViolations = 0;
	local numeric m, n;
	for m = 1 to rows {
		SetStatusBar(m, rows);
		for n = 1 to cols {
			point = ObjectToMap(outR, n, m, outG);
			# Convert the point to the SRTM CRS if not the same
			if (!same) {
				point = trans.ConvertPoint2DFwd(point);
			}
			point = MapToObject(refG, point.x, point.y, RefR);
			point.x = round(point.x);
			point.y = round(point.y);
			# Check if the point is valid
			if ( point.x >= 1 and point.x <= RefR.$Info.NumCols) {
				if ( (point.y >= 1) && (point.y <= RefR.$Info.NumLins) ) {
					# Check if the difference in elevation values is greater then the threshold
					if ( abs(outR[m,n] - (RefR[point.y, point.x] * RefDataScale)) > threshold) {
						# Set the cell value to null and update the count
						if (HasNullValue(outR)) {
							outR[m,n] = NullValue(outR);
						}
						if (HasNullMask(outR)) {
							outR[m,n] = null;
						}
						numViolations++;
					}
				}
			}
		}
	}
	print("   Total violations replaced with null: " + NumToStr(numViolations) + "\n");
}
######################################################################################################
# Is true if all the surrouding cells from the specified cell are null or equal to the passed value.
func AreAllSurroundingSameAsValueOrNull(numeric i, numeric j, numeric value) {
 
	local numeric valid = true;
	# Checks if cell exists.  Returns false if the existing cell is Null
	if ((outR[i-1,j+1] != value) & !IsNull(inR[i-1,j+1]))  valid = false;
	if ((outR[i-1,j  ] != value) & !IsNull(inR[i-1,j  ]))  valid = false;
	if ((outR[i-1,j-1] != value) & !IsNull(inR[i-1,j-1]))  valid = false;
	if ((outR[i  ,j+1] != value) & !IsNull(inR[i  ,j+1]))  valid = false;
	if ((outR[i  ,j-1] != value) & !IsNull(inR[i  ,j-1]))  valid = false;
	if ((outR[i+1,j+1] != value) & !IsNull(inR[i+1,j+1]))  valid = false;
	if ((outR[i+1,j  ] != value) & !IsNull(inR[i+1,j  ]))  valid = false;
	if ((outR[i+1,j-1] != value) & !IsNull(inR[i+1,j-1]))  valid = false;
	return valid;
}
######################################################################################################
# Is true if all the surrounding cells from the specified cell are equal to the passed value.
func AreAllSurroundingSameAsValue(numeric i, numeric j, numeric value) {
	# Check each surrouding cell 
	if ( IsNull(outR[i-1,j-1]) ) {
		return false;
	} else if ( outR[i-1,j-1] != value ) {
		return false;
	}
	if ( IsNull(outR[i-1,j  ]) ) {
		return false;
	} else if ( outR[i-1,j  ] != value ) {
		return false;
	}
	if ( IsNull(outR[i-1,j+1]) ) {
		return false;
	} else if ( outR[i-1,j+1] != value ) {
		return false;
	}
	if ( IsNull(outR[i  ,j-1]) ) {
		return false;
	} else if ( outR[i  ,j-1] != value ) {
		return false;
	}
	if ( IsNull(outR[i  ,j+1]) ) {
		return false;
	} else if ( outR[i  ,j+1] != value ) {
		return false;
	}
	if ( IsNull(outR[i+1,j-1]) ) {
		return false;
	} else if ( outR[i+1,j-1] != value ) {
		return false;
	}
	if ( IsNull(outR[i+1,j  ]) ) {
		return false;
	} else if ( outR[i+1,j  ] != value ) {
		return false;
	}
	if ( IsNull(outR[i+1,j+1]) ) {
		return false;
	} else if ( outR[i+1,j+1] != value ) {
		return false;
	}
	return true;
}
######################################################################################################
# This is a recursive function.  It is called when a null cell is found.  It uses the sequence of 
# travel specified below when traversing around the raster.  As it travels around the boundry of the
# null void, it checks to see if the surrounding cells are the same constant value as the cell value
# to the left of the initial null cell.  If the function ends up at the same point it started and did
# not fail any of it's surrounding cell checks, it goes back through the same path that it traveled
# and replaces each cell with the constant value.  If a void still exists within this traversed path,
# it will be filled in as the original function that calls this recursive function traverses the SRTM
# raster.
func RecursiveBorderCheck(numeric i, numeric j, numeric start_i, start_j, string traveled, numeric value, numeric firstCall) {
	# If the current position one cell to the right of starting cell and it is not the first call of
	# the function, it means that it has gone all the way around the border and each border cell has
	# the same constant value.  Returns true so it recusively fills in each of the cells that the
	# function was called on with the constant value.
	if (( i == start_i) & (j-1 == start_j) & (!firstCall)) {
		return true;
	}
	# Check if the surrounding cells pass the constant value or null test
	if (AreAllSurroundingSameAsValueOrNull(i, j, value)) {
		# Determine which direction to try and travel based on the sequence below:
		#
		# Sequence of travel:  
		# If the function was called by traveling to the:
		#  RIGHT - try in order of - UP,    RIGHT, DOWN,  LEFT
		#  DOWN  - try in order of - RIGHT, DOWN,  LEFT,  UP
		#  LEFT  - try in order of - DOWN,  LEFT,  UP,    RIGHT
		#  UP    - try in order of - LEFT,  UP,    RIGHT, DOWN
		# Check which direction the calling funtion traveled to the current cell
		if (traveled == "RIGHT") {
			# If the cell UP is null, go that way
			if (IsNull(outR[i-1, j])) {
				# Call this function and go that way
				if (RecursiveBorderCheck(i-1, j, start_i, start_j, "UP", value, 0)) {
					# If it returned true, it is traversing back after passing the constant border test
					outR[i,j] = value;
					return true;
				} else {
					# Failed the constant border test, just exit out of the function
					return false;
				}
			# If the cell RIGHT is null, go that way
			} else if (IsNull(outR[i,j+1])) {
				if (RecursiveBorderCheck(i, j+1, start_i, start_j, "RIGHT", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			# If the cell DOWN is null, go that way
			} else if (IsNull(outR[i+1,j])) {
				if (RecursiveBorderCheck(i+1, j, start_i, start_j, "DOWN", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			# If the cell LEFT is null, go that way
			} else if (IsNull(outR[i,j-1])) {
				if (RecursiveBorderCheck(i, j-1, start_i, start_j, "LEFT", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} 
		}
		# Comments similar to the above
		if (traveled == "DOWN") {
			if (IsNull(outR[i, j+1])) {
				if (RecursiveBorderCheck(i, j+1, start_i, start_j, "RIGHT", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i+1,j])) {
				if (RecursiveBorderCheck(i+1, j, start_i, start_j, "DOWN", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i,j-1])) {
				if (RecursiveBorderCheck(i, j-1, start_i, start_j, "LEFT", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i-1,j])) {
				if (RecursiveBorderCheck(i-1, j, start_i, start_j, "UP", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			}
		}
		# Comments similar to the above
		if (traveled == "LEFT") {
			if (IsNull(outR[i+1, j])) {
				if (RecursiveBorderCheck(i+1, j, start_i, start_j, "DOWN", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i,j-1])) {
				if (RecursiveBorderCheck(i, j-1, start_i, start_j, "LEFT", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i-1,j])) {
				if (RecursiveBorderCheck(i-1, j, start_i, start_j, "UP", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i,j+1])) {
				if (RecursiveBorderCheck(i, j+1, start_i, start_j, "RIGHT", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			}
		}
		# Comments similar to the above
		if (traveled == "UP") {
			if (IsNull(outR[i, j-1])) {
				if (RecursiveBorderCheck(i, j-1, start_i, start_j, "LEFT", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i-1,j])) {
				if (RecursiveBorderCheck(i-1, j, start_i, start_j, "UP", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i,j+1])) {
				if (RecursiveBorderCheck(i, j+1, start_i, start_j, "RIGHT", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			} else if (IsNull(outR[i+1,j])) {
				if (RecursiveBorderCheck(i+1, j, start_i, start_j, "DOWN", value, 0)) {
					outR[i,j] = value;
					return true;
				} else {
					return false;
				}
			}
		}
	} else {
		# Failed the constant surrounding values check, exit out.
		return false;
	}
}
######################################################################################################
# This procedure traverses through the SRTM raster, and if it finds a null value, it calls a recursive
# function to travel around the border and fill in the void if all surrouding cells have the same
# cell value.
proc FillVoidsWithConstantBoundaryValues( ) {
	local numeric i, j;
	local numeric value, total = 0;
	print("Filling null voids that have a constant value on all borders\n");
	SetStatusMessage("Filling null voids with constant boundaries");
	# Fill any voids that have a constant boundary surrouding.  Will "errode" in one pixel.
	# Continuing passes through the raster will keep filling in one pixel at a time.  This
	# may leave a single cell remaining null based on original shape of the void.
	# WARNING:  This may partialy or completely cover an island with water if the bounding
	#           value around the island (void) is constant.
	# Traverse through the SRTM raster, except for the outside edge.  The algorighm can't determine if
	# the "real world elevation" value beyond the raster has the same value since the data does not 
	# exist in this raster.
	for i = 2 to rows-1 {
		SetStatusBar(i, rows-1);
		for j = 2 to cols-1 {
			if (IsNull(outR[i,j])) {
				# If the cell to the left and up are not null, get the value and start the processes
				if ((!IsNull(outR[i,j-1])) & (!IsNull(outR[i-1,j]))) {
					value = outR[i,j-1];
					# Do an initial check of the surrouding cells and call the function if it passes
					if (AreAllSurroundingSameAsValueOrNull(i, j, value)) {
						# Keep a report for each successful filling.  (multiple sucesses may be recorded
						# since the void is filled in one boudary ring at a time)
						total += RecursiveBorderCheck(i, j, i, j-1, "RIGHT", value, 1);
					}
				}
			}
		}
	}
	print("   Number of null voids filled: " + NumToStr(total) + "\n");
	total = 0;
	# Fill any single cells remaining that have constant values surrounding it.  This is done to fill
	# in any single cells that the previous recursive function missed since it cannot return true on
	# the first call to itself.
	for i = 1 to rows-1 {
		SetStatusBar(i, rows-1);
		for j = 1 to cols-1 {
			# Check if the cell is null and the cell to the left is not null
			if ( (IsNull(outR[i,j])) & (!IsNull(outR[i,j-1])) ) {
				value = outR[i,j-1];
				# Check if all surrounding cells have the same value
				if (AreAllSurroundingSameAsValue(i, j, value)) {
					# Fill in the cell and update the count
					outR[i,j] = value;
					total++;
				}
			}
		}
	}
	print("   Number of single null cells filled: " + NumToStr(total) + "\n");
}
######################################################################################################
# Assign the global variables the values set by the user in the dialog
proc SetUserSpecifiedGlobalValues( ) {
	numRequiredAround = mainDLG.GetCtrlByID("id_NUM_REQUIRED_SURROUNDING").GetValueNum();
	numReferenceDEMs = mainDLG.GetCtrlByID("id_NUM_REF_DEM").GetValueNum();
	cellBufferDist = mainDLG.GetCtrlByID("id_NUM_BUFFER_ZONE").GetValueNum();
	threshold = mainDLG.GetCtrlByID("id_THRESHOLD_VALUE").GetValueNum();
}
######################################################################################################
# This is the main procedure that is called when the user presses RUN with the appropriate settings
# set in the main dialog.  It calls functions based on the processes that the user has requested be
# run on the data provided.
proc RunVoidFillingProcess( ) {
	local numeric nullCellsExist = 1;
	local class GUI_CTRL_TOGGLEBUTTON saveToggle = mainDLG.GetCtrlByID("id_SAVE_TEMP_FILE_TOGGLE");
	SetUserSpecifiedGlobalValues();
	print("Starting...\n");
	# Check input SRTM raster to see if it has any null cells
	OpenRaster(inR, InputRasterFilename$, InputRasterObjectName$);
	if (!HasNull(inR)) {
		PopupMessage("This raster does not have any recognized Null values");
		print("Done!\n");
		return;
	}
	# If the temporary working project file is not specified by the user, create one
	if (!saveToggle.GetValue()) {
		TempSRTMfile = _context.ScriptDir + "/SRTMvoidfillingTempFile.rvc";
		if (fexists(TempSRTMfile)) {
			DeleteFile(TempSRTMfile);
		}
		CreateProjectFile(TempSRTMfile, "Temporary working directory for SRTM void filling SML script");
	}
	OpenRaster(outR, OutputRasterFilename$, OutputRasterObjectName$);
	# Make a copy of the input raster.  All procedures will only modify this copy
	DeleteSubobjects(outR);
	CopySubobjects(inR, outR, "ALL");
	if(HasNullValue(inR)) {
		SetNull(outR, NullValue(inR));
	}
	outR = inR;
	# Flush the raster object to recognize the null mask if it has one
	CloseRaster(outR);
	OpenRaster(outR, OutputRasterFilename$, OutputRasterObjectName$);
	# Call the procedure to fill single null cells if selected
	if (mainDLG.GetCtrlByID("id_FILL_SINGLE_NULLS_TOGGLE").GetValueNum()) {
		local numeric negNullTotal = 0;
		local numeric temp = 0;
		print("Filling single null cells\n");
		# Traverse the SRTM raster until no changes are made
		while ( (temp = CheckNegligibleNulls(8 - numRequiredAround )) > 0 ) {
			negNullTotal = negNullTotal + temp;
		}
		print("       Total number changed: " + NumToStr(negNullTotal) + "\n");
		# Check to see if there are any remaining null cells in the raster
		if (NumNullCellsInRaster(outR) == 0) {
			print("There are no remaining null cells in the raster.  Remaining filling methods will be skipped.\n");
			nullCellsExist = 0;
		}
	}
	# Call the procedure to fill null voids with constant boundaries if selected
	if (mainDLG.GetCtrlByID("id_FILL_CONSTANT_NULL_VOIDS_TOGGLE").GetValueNum() and nullCellsExist) {
		FillVoidsWithConstantBoundaryValues();
		# Check to see if there are any remaining null cells in the raster
		if (NumNullCellsInRaster(outR) == 0) {
			print("There are no remaining null cells in the raster.  Remaining filling methods will be skipped.\n");
			nullCellsExist = 0;
		}
	}
	# Call the procedure to fill null voids with additional data if selected
	if (mainDLG.GetCtrlByID("id_FILL_VOIDS_WITH_REF_DEM_TOGGLE").GetValueNum() and nullCellsExist) {
		local numeric i;
		# For each reference raster that was selected
		for i = 0 to numReferenceDEMs-1 {
			if (nullCellsExist) {
				OpenRaster(RefR, RefDEMFilenames$[i], RefDEMObjectNames$[i]);
				RefDataScale = RefR.$Info.DataScale;
				# Call the procedure to replace cells that violate the maximum elevation difference
				if (mainDLG.GetCtrlByID("id_USE_THRESHOLD_TOGGLE").GetValueNum()) {
					RemoveThresholdViolations();
				}
				# Create working raster and vectors and fill holes with reference data
				CreateNullRasterVector();
				FillWithRefRaster();
				# Call the procedure to blend around the null voids if selected
				if (mainDLG.GetCtrlByID("id_USE_BUFFER_ZONE_TOGGLE").GetValueNum()) {
					BlendWithRefRaster();
				}
				CloseRaster(RefR);
				# Check to see if there are any remaining null cells in the raster
				if (NumNullCellsInRaster(outR) == 0) {
					print("There are no remaining null cells in the raster.  Remaining filling methods will be skipped.\n");
					nullCellsExist = 0;
				}
			}
		}
	}
	# Update subobjects for new SRTM raster
	CreateHistogram(outR);
	CreatePyramid(outR);
	print("Void-filled SRTM Raster saved as: " + outR.$Info.Name);
	print("Located in project file: " + outR.$Info.Filename + "\n"); 
	CloseRaster(inR);
	CloseRaster(outR);
	# Delete the working directory if not selected to save
	if (!saveToggle.GetValue()) {
		DeleteFile(TempSRTMfile);
	}
	print("Done!\n");
	return;
}
######################################################################################################
######################################################################################################
# Dialog Related Functions
######################################################################################################
######################################################################################################
# Called when status dialog is created to start the void filling processes
proc OnProgressOpen( ) {
	RunVoidFillingProcess();
	progressDLG.Close(0);
}
######################################################################################################
# Called when status dialog is created to check if the selected input has any valid null cells
proc OnVerifyOpen( ) {
	# Check to see if there are any null cells in the raster
	local class RASTER checkR;
	OpenRaster(checkR, InputRasterFilename$, InputRasterObjectName$);
	numValidNulls = NumNullCellsInRaster(checkR);
	CloseRaster(checkR);
	progressDLG.Close(0);
}
######################################################################################################
# Called when "Run..." is pressed.  Checks to make sure settings are valid before starting the filling
# procedures.
proc OnRunPressed( ) {
	local class GUI_CTRL_PUSHBUTTON buttonRun = mainDLG.GetCtrlByID("id_RUN_BUTTON");
	buttonRun.SetEnabled(0);
	local class GUI_CTRL_TOGGLEBUTTON toggleSingle = mainDLG.GetCtrlByID("id_FILL_SINGLE_NULLS_TOGGLE");
	local class GUI_CTRL_TOGGLEBUTTON toggleConst = mainDLG.GetCtrlByID("id_FILL_CONSTANT_NULL_VOIDS_TOGGLE");
	local class GUI_CTRL_TOGGLEBUTTON toggleDEM = mainDLG.GetCtrlByID("id_FILL_VOIDS_WITH_REF_DEM_TOGGLE");
	if (!toggleSingle.GetValue() and !toggleConst.GetValue() and !toggleDEM.GetValue()) {
		string msg = "Error:  Please select at least one void filling procedure: \n\n";
		msg = msg + "   1) Interpolate single null cells\n";
		msg = msg + "   2) Fill null voids surrounded by constant value\n";
		msg = msg + "   3) Fill null voids using reference DEM rasters";
		PopupMessage(msg);
		buttonRun.SetEnabled(1);
		return;
	}
	# If using reference rasters, make sure the correct number have been selected
	if ( toggleDEM.GetValue() ) {
		local class GUI_CTRL_LISTBOX list = mainDLG.GetCtrlByID("id_REF_DEM_LIST");
		local class GUI_CTRL_EDIT_NUMBER number = mainDLG.GetCtrlByID("id_NUM_REF_DEM");
		if ( list.GetCount() != number.GetValue() ) {
			PopupMessage("Please select the correct number of DEMs you wish to use as reference");
			buttonRun.SetEnabled(1);
			return;
		}
	}
	errXML = progressDOC.Parse(progressxmlCODE$);
	if ( errXML < 0 ) {
		PopupError( errXML );	# pop up an error dialog
		mainDLG.Close(1);
		Exit( );
	}
	progressNODE = progressDOC.GetElementByID( "VERIFY_DIALOG" );
	if ( progressNODE == 0 ) {
		PopupMessage( "Could not find progress dialog node in XML document" );
		mainDLG.Close(1);
		Exit();
	}
	progressDLG.SetXMLNode(progressNODE);
	progressDLG.DoModal( mainDLG.GetWidget() );
	if (numValidNulls == 0) {
		print("There are no recognized null cells in the raster.\n");
		PopupMessage("There are no recognized null cells in the raster.");
		buttonRun.SetEnabled(1);
		return;
	}
	progressNODE = progressDOC.GetElementByID( "PROGRESS_DIALOG" );
	if ( progressNODE == 0 ) {
		PopupMessage( "Could not find progress dialog node in XML document" );
		mainDLG.Close(1);
		Exit();
	}
	progressDLG.SetXMLNode(progressNODE);
	# Set up a timer to record completion time
	local class TIMER time;
	time.Running = 0;
	time.Value = 0;
	time.Running = 1;
	progressDLG.DoModal( mainDLG.GetWidget() );
	time.Running = 0;
	printf("Time to complete: %2d minutes %0.2d seconds", floor(time.Value / 60), floor(time.Value % 60));
	mainDLG.Close(1);
}
######################################################################################################
# Called when "Canel" is pressed.  Closes dialog and exits script.
proc OnCancelPressed( ) {
	mainDLG.Close(1);
}
######################################################################################################
# Called when "Input SRTM raster" is pressed.  Opens dialog to select raster.
proc OnGetInputSRTMPressed( ) {
	local class RASTER R;
	GetInputRaster(R);
	InputRasterFilename$ = R.$Info.Filename;
	InputRasterObjectName$ = R.$Info.Name;
	rows = NumLins(R);
	cols = NumCols(R);
	initType = RastType(R);
	# Update the dialog
	local class GUI_CTRL_EDIT_STRING text = mainDLG.GetCtrlByID("id_INPUT_SRTM_RASTER");
	text.SetValue(InputRasterFilename$ + " / " + InputRasterObjectName$, 0);
	mainDLG.GetCtrlByID("id_GET_OUTPUT_SRTM_BUTTON").SetEnabled(1);
	CloseRaster(R);
}
######################################################################################################
# Called when "Output SRTM raster" is pressed.  Opens dialog to select/create output raster.
proc OnGetOutputSRTMPressed( ) {
	local class RASTER Rin, Rout;
	# Get the raster to save result too
	GetOutputRaster(Rout, rows, cols, initType);
	OutputRasterFilename$ = Rout.$Info.Filename;
	OutputRasterObjectName$ = Rout.$Info.Name;
	CloseRaster(Rout);
	# Update the dialog
	local class GUI_CTRL_EDIT_STRING text = mainDLG.GetCtrlByID("id_OUTPUT_SRTM_RASTER");
	text.SetValue(OutputRasterFilename$ + " / " + OutputRasterObjectName$, 0);
	mainDLG.GetCtrlByID("id_RUN_BUTTON").SetEnabled(1);
}
######################################################################################################
# Called when toggle is changed.  Updates dialog settings.
proc OnUseSingleCellToggleChanged( ) {
	local class GUI_CTRL_TOGGLEBUTTON toggle = mainDLG.GetCtrlByID("id_FILL_SINGLE_NULLS_TOGGLE");
	mainDLG.GetCtrlByID("id_NUM_REQUIRED_SURROUNDING_LABEL").SetEnabled(toggle.GetValue());
	mainDLG.GetCtrlByID("id_NUM_REQUIRED_SURROUNDING").SetEnabled(toggle.GetValue());
}
######################################################################################################
# Called when toggle is changed.  Updates dialog settings.
proc OnUseRefDemToggleChanged( ) {
	local class GUI_CTRL_TOGGLEBUTTON toggle = mainDLG.GetCtrlByID("id_FILL_VOIDS_WITH_REF_DEM_TOGGLE");
	local class GUI_CTRL_EDIT_NUMBER number = mainDLG.GetCtrlByID("id_NUM_REF_DEM");
	local class GUI_CTRL_LISTBOX list = mainDLG.GetCtrlByID("id_REF_DEM_LIST");
	# Manage the listbox
	mainDLG.GetCtrlByID("id_NUM_REF_DEM_LABEL").SetEnabled(toggle.GetValue());
	number.SetEnabled(toggle.GetValue());
	number.ClearValue(0);
	list.DeleteAllItems();
	mainDLG.GetCtrlByID("id_GET_REF_DEM_BUTTON").SetEnabled(0);
	# Manage the sub-procedure settings for using reference rasters
	if (toggle.GetValue() == 0) {
		local class GUI_CTRL_TOGGLEBUTTON threshToggle = mainDLG.GetCtrlByID("id_USE_THRESHOLD_TOGGLE");
		threshToggle.SetValue(0,1);
		threshToggle.SetEnabled(0);
		local class GUI_CTRL_TOGGLEBUTTON bufferToggle = mainDLG.GetCtrlByID("id_USE_BUFFER_ZONE_TOGGLE");
		bufferToggle.SetValue(0,1);
		bufferToggle.SetEnabled(0);
	}
}
######################################################################################################
# Called when "Get DEMs..." is pressed.  Allows user to select the number of reference DEMs that they
# specified in the number box.
proc OnGetRefDEMRastersPressed( ) {
	local class GUI_CTRL_EDIT_NUMBER number = mainDLG.GetCtrlByID("id_NUM_REF_DEM");
	local class GUI_CTRL_LISTBOX list = mainDLG.GetCtrlByID("id_REF_DEM_LIST");
	list.DeleteAllItems();
	RefDEMFilenames$.Clear();
	RefDEMObjectNames$.Clear();
	local class RASTER R;
	local numeric i;
	local numeric numRefDems = number.GetValue();
	# For the number of reference DEMs the user specified they want to use
	for i = 0 to numRefDems-1 {
		GetInputRaster(R);
		RefDEMFilenames$.AddToEnd(R.$Info.Filename);
		RefDEMObjectNames$.AddToEnd(R.$Info.Name);
		CloseRaster(R);
		list.AddItem(RefDEMFilenames$[i] + " / " + RefDEMObjectNames$[i]);
	}
	# Update sub-procedures
	local class GUI_CTRL_TOGGLEBUTTON threshToggle = mainDLG.GetCtrlByID("id_USE_THRESHOLD_TOGGLE");
	threshToggle.SetEnabled(1);
	threshToggle.SetValue(0,1);
	local class GUI_CTRL_TOGGLEBUTTON bufferToggle = mainDLG.GetCtrlByID("id_USE_BUFFER_ZONE_TOGGLE");
	bufferToggle.SetEnabled(1);
	bufferToggle.SetValue(0,1);
}
######################################################################################################
# Called after user changes value of the number of reference DEMs they wish to use.
proc OnNumDEMChanged ( ) {
	local class GUI_CTRL_EDIT_NUMBER number = mainDLG.GetCtrlByID("id_NUM_REF_DEM");
	mainDLG.GetCtrlByID("id_GET_REF_DEM_BUTTON").SetEnabled(1);
	local class GUI_CTRL_LISTBOX list = mainDLG.GetCtrlByID("id_REF_DEM_LIST");
	list.DeleteAllItems();
	RefDEMFilenames$.Clear();
	RefDEMObjectNames$.Clear();
	# Call procedure to start selecting reference DEMs
	OnGetRefDEMRastersPressed();
}
######################################################################################################
# Called when toggle is changed.  Updates dialog settings.
proc OnUseThresholdToggleChanged( ) {
	local class GUI_CTRL_TOGGLEBUTTON toggle = mainDLG.GetCtrlByID("id_USE_THRESHOLD_TOGGLE");
	mainDLG.GetCtrlByID("id_THRESHOLD_VALUE_LABEL").SetEnabled(toggle.GetValue());
	mainDLG.GetCtrlByID("id_THRESHOLD_VALUE").SetEnabled(toggle.GetValue());
}
######################################################################################################
# Called when toggle is changed.  Updates dialog settings.
proc OnUseBufferZoneToggleChanged( ) {
	local class GUI_CTRL_TOGGLEBUTTON toggle = mainDLG.GetCtrlByID("id_USE_BUFFER_ZONE_TOGGLE");
	mainDLG.GetCtrlByID("id_NUM_BUFFER_ZONE_LABEL").SetEnabled(toggle.GetValue());
	mainDLG.GetCtrlByID("id_NUM_BUFFER_ZONE").SetEnabled(toggle.GetValue());
}
######################################################################################################
# Called when "Project File..." is pressed.  Opens dialog to select project file in which to save the
# temporary working vectors and rasters.
proc OnGetTempFilePressed( ) {
	local class GUI_CTRL_EDIT_STRING text = mainDLG.GetCtrlByID("id_TEMP_FILE");
	TempSRTMfile = GetOutputFileName(_context.ScriptDir, "Select project file to save temp files to:", "rvc");
	# Check if the user pressed "Cancel" in the dialog
	if (TempSRTMfile == _context.ScriptDir) {
		local class GUI_CTRL_TOGGLEBUTTON toggle = mainDLG.GetCtrlByID("id_SAVE_TEMP_FILE_TOGGLE");
		mainDLG.GetCtrlByID("id_SAVE_TEMP_FILE_BUTTON").SetEnabled(0);
		toggle.SetValue(0, 0);
		text.ClearValue(0);
		text.SetEnabled(0);
		return;
	} 
	# Delete the file if it exists.  The user would have recieved a "Overwrite this file?" warning.
	if (fexists(TempSRTMfile)) {
		DeleteFile(TempSRTMfile);
	}
	CreateProjectFile(TempSRTMfile, "Created by SRTM Void Filling Script to save temporary files");
	text.SetValue(TempSRTMfile,0);
} 
######################################################################################################
# Called when toggle is changed.  Updates dialog settings.
proc OnSaveTempFileToggleChanged( ) {
	local class GUI_CTRL_TOGGLEBUTTON toggle = mainDLG.GetCtrlByID("id_SAVE_TEMP_FILE_TOGGLE");
	if (toggle.GetValue()) {
		OnGetTempFilePressed();
	}
	mainDLG.GetCtrlByID("id_SAVE_TEMP_FILE_BUTTON").SetEnabled(toggle.GetValue());
	mainDLG.GetCtrlByID("id_TEMP_FILE").SetEnabled(toggle.GetValue());
}
######################################################################################################
# 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( ) {
	errXML = mainDOC.Parse(mainxmlCODE$);
	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();
	}
}
######################################################################################################