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