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

HOME

CONTACT US

CURRENT RELEASE
  TNT 2013

DEVELOPMENT VERSION
  TNT 2014

TNTmips Pro
PRIOR RELEASES
  TNT 2012

FREE SOFTWARE
  TNTmips Free
  TNTatlas
  TNTsdk

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

DOCUMENTATION
  TNTmips Tutorials
  Tutorial Datasets
  Technical Guides
  Scripts
  Quick Guides

MORE INFO
  Download FAQs
  FTP
  Download Managers
  Find Reseller

SITE MAP


surfcurv.sml


### 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");


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

25 March 2009

page update: 26 May 11