surfcurv.sml

  Download

More scripts: Advanced

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
### surfcurve.sml
### Computes curvatures (profile = vertical and plan = horizontal) 
### for each DEM raster cell.  Profile curvature is positive for
### convex upward surfaces; plan curvature is positive for convex
### downhill surfaces. 
### Curvatures are computed for each DEM cell using a 3 x 3 kernel
### by computing a local best-fit quadratic surface for the nine-cell 
### neighborhood.  The equation of this surface can be written:
### z = a*x^2 + b*y^2 + c*xy + d*x + ee*y + f
### The best-fit surface is not constrained to exactly fit any of the
### DEM values in the neighborhood; it provides a smoothed surface
### that minimizes the impact of elevation errors in the DEM. 
### Algorithm modified from:
### Evans, I.S. (1979) An integrated system of terrain analysis and slope mapping. 
### Final report on grant DA-ERO-591-73-G0040, University of Durham, England.
### http://www.soi.city.ac.uk/~jwo/phd/04param.php3
### See also Schmidt, J., Evans, I.S., and Brinkmann, J. (2003), Comparison of
### polynomial models for land surface curvature calculation.  International
### Journal of Geographical Information Science, v.17, no.8, p. 797-814.
#### Global variables #########################################
 
numeric scale;
### With scale = 1, units of curvature are radians / meter.
### With scale = 100, units of curvature are radians / 100 m.
 
numeric a, b, c, d, ee, f;		# coefficients of quadratic equation
raster DEM;							# input elevation surface raster
raster Profile, Plan;			# output curvature rasters
numeric lin, col;					# line and column counters
numeric cellX, cellY;			# DEM cell sizes in column and line directions
numeric prfdenom, plndenom;	# values of denominators of the two curvature expressions
numeric doSmooth;					# flag for whether or not to used averaged DEM value
array numeric kernel[3,3];
# Procedure to read 3x3 kernel of DEM values
# surrounding current cell.  Notation of cells in kernel is as follows:
#	 ----------------
#	 |	z1 | z2 | z3 |
#	 ----------------
#	 |	z4	| z5 | z6 |
#   ----------------
#	 |	z7	| z8 | z9 |
#	 ----------------
proc readkernel (class raster K, numeric line, numeric colm){
	numeric z1 = K[line-1, colm-1];
	numeric z2 = K[line-1, colm];
	numeric z3 = K[line-1, colm+1];
	numeric z4 = K[line, colm-1];
	numeric z5 = K[line, colm];
	numeric z6 = K[line, colm+1];
	numeric z7 = K[line+1, colm-1];
	numeric z8 = K[line+1, colm];
	numeric z9 = K[line+1, colm+1];
	}  
# Function to check surrounding area of Kernel.  If no difference, 
# there is no need to do some computations.
# Also used as a safety against divide by zero.
func levelcheck() {
	if (z5 != z1) return true;
	if (z5 != z2) return true;
 	if (z5 != z3) return true;
	if (z5 != z4) return true;
	if (z5 != z6) return true;
	if (z5 != z7) return true;
	if (z5 != z8) return true; 
	if (z5 != z9) return true;
	return false;
}
# Function to check kernel values for null cells.
# Returns false if any cell is null. 
func nullcheck() {
	if (IsNull(z1) ) return false;
	if (IsNull(z2) ) return false;
	if (IsNull(z3) ) return false;
	if (IsNull(z4) ) return false;
	if (IsNull(z5) ) return false;
	if (IsNull(z6) ) return false;
	if (IsNull(z7) ) return false;
	if (IsNull(z8) ) return false;
	if (IsNull(z9) ) return false;
	return true;
	}
## procedure to compute coefficients for local quadratic surface
## dx and dy are line and column cell sizes
proc coeff(numeric dx, numeric dy) {
	a = (z1 + z3 + z4 + z6 + z7 + z9)/(6 * dx^2) - (z2 + z5 + z8)/(3 * dx^2);
	b = (z1 + z2 + z3 + z7 + z8 + z9)/(6 * dy^2) - (z4 + z5 + z6)/(3 * dy^2);
	c = (z3 + z7 - z1 - z9)/(4 * dx * dy);
	d = (z3 + z6 + z9 - z1 - z4 - z7)/(6 * dx);
	ee = (z1 + z2 + z3 - z7 - z8 -z9)/(6 * dy);
	}
proc makeCurv(class raster D) {
	for each D[lin,col] {
		if (lin != 1 && lin != D.$Info.NumLins && col != 1 && col != D.$Info.NumCols) {  ## don't process edge cells
			readkernel(D, lin, col);		# read 3x3 kernel values and check for null cells
			if ( nullcheck() ) {
				if( levelcheck() ) {		## don't process cell if 3x3 kernel is level
					coeff(cellX, cellY);		## call procedure to compute coefficients for quadratic surface
					# compute denominators for profile and plan curvature expressions to check for division by zero
					prfdenom = round(10^7 * ( (sqr(ee) + sqr(d) ) * ( (1 + sqr(d) + sqr(ee) ) ^ 1.5)) ) * 10^-7;
					plndenom = round(10^7 * ( (sqr(ee) + sqr(d) )^1.5) ) * 10^-7;
					if (prfdenom == 0 || plndenom == 0) {		## avoid division by zero (can occur for summits, depressions, and saddles)
						if ( (a > 0 && b > 0) || (a < 0 && b < 0) ) { 
							Profile[lin, col] = - (a + b) * scale;				## case for summit or depression
							Plan[lin, col] = -32768;
							}
						else {
							Profile[lin,col] = 0;					## case for saddles
							Plan[lin, col] = 0;
							}
						}		# end if (XXXdenom == 0)
					else {		# curvature computation for typical cells
						Profile[lin, col] = scale * -2 * ( a * sqr(d) + b * sqr(ee) + c * d * ee) / prfdenom;
						Plan[lin, col] = scale * -2 * (b * sqr(d) + a * sqr(ee) - c * d * ee) / plndenom;
						}
					} 
				else {
					Plan[lin, col] = -32768;			## case for flat kernel area
					Profile[lin, col] = 0;
					}
				} # end if ( levelcheck() )
			else {
				# case for kernels including null cells, not enough data to compute curvature
				Plan[lin,col] =-32768;
				Profile[lin,col] =-32768;
				}
			} # end if ( nullcheck() )
	else {
		# case for edge cells, not enough data to compute curvature
		Plan[lin,col] = -32768;
		Profile[lin,col] = -32768;
		}
	}
}
#############################################################
### Main program
GetInputRaster(DEM);
cellX = ColScale(DEM);
cellY = LinScale(DEM);
GetOutputRaster(Profile, DEM.$Info.NumLins, DEM.$Info.NumCols, "32-bit float");
GetOutputRaster(Plan, DEM.$Info.NumLins, DEM.$Info.NumCols, "32-bit float");
SetNull(Profile,-32768);
SetNull(Plan,-32768);
scale = PopupNum("Enter 1 to compute curvature in radians/m or \nenter 100 for curvature in radians/100 m", 1, 100, 0);
doSmooth = PopupYesNo("Use averaged DEM values for curvature?", 1);
if (doSmooth == 1) {
	class RASTER Temp;
	# pop up dialog window to get size of filter window for averaging DEM values
	local numeric filtsize = PopupNum("Size of averaging window in cells? \n(3, 5, 7, or 9)", 5, 3, 9, 0);
	if ( filtsize % 2 == 0 ) then		## check for even number entered as window size
		filtsize = filtsize + 1;
	CreateTempRaster(Temp, DEM.$Info.NumLins, DEM.$Info.NumCols, DEM.$Info.Type);
	## compute averaged DEM value for non-null cell, else assign null value to Temp raster
	numeric i, j;
	for i = 1 to DEM.$Info.NumLins  {
		for j = 1 to DEM.$Info.NumCols {
			if ( IsNull(DEM[i,j]) ) then
				Temp[i,j] = -32768;
			else
				Temp[i,j] = FocalMean(DEM, filtsize, filtsize, i, j);
			}
		}
	SetNull(Temp, -32768);
	makeCurv(Temp);
	CloseRaster(Temp);
	}	# end of if (doSmooth() )
else
	makeCurv(DEM);
for each Profile {
	if (Profile < -32768) then
		Profile = -32768;
	if (Plan < -32768) then
		Plan = -32768;
	}
SetNull(Profile, -32768);
SetNull(Plan, -32768);
CreateHistogram(Profile);
CreateHistogram(Plan);
CreatePyramid(Profile);
CreatePyramid(Plan);
CopySubobjects(DEM, Profile, "georef");
CopySubobjects(DEM, Plan, "georef");