utest.sml

  Download

More scripts: Display Toolbar

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
# The following symbols are predefined
#    class VIEW View            {use to access the view the tool script is attached to}
#    class GROUP Group          {use to access the group being viewed if the script is run from a group view}
#    class LAYOUT Layout        {use to access the layout being viewed if the script is run from a layout view} 
# 
# The following values are also predefined and are valid when the various On...() 
# functions are called which deal with pointer and keyboard events. 
#    number PointerX            Pointer X coordinate within view in pixels 
#    number PointerY            Pointer Y coordinate within view in pixels 
# Variable declarations
class GRE_GROUP activegroup;
class GRE_LAYER_VECTOR vectorLayer, pointLayer, polyLayer;
class Vector pointVector;
class Vector lineVector;
class Vector polyVector, tmpVector;
class Raster rast;
class REGION2D regionIn, regionOut;
numeric n1=0, n2=0;
class DBTABLEINFO pointTable, polyTable;
class GUI_DLG dlgwin;
class GUI_CTRL widthCtrl, lengthCtrl, pointCtrl, polyCtrl;
numeric ALPHA = .05;
string pointLayerName;
numeric currentRun = 1;
# tables to use for critical U values
numeric MAX_TABLE_SIZE = 20;
array numeric alpha05[MAX_TABLE_SIZE,MAX_TABLE_SIZE];
array numeric alpha01[MAX_TABLE_SIZE,MAX_TABLE_SIZE];
proc popupMessage(numeric n)
{
	PopupMessage(NumToStr(n));
}
# Initialize the critical U values lookup tables for the Mann-Whitney U test
# At the time of this script the tables were available at the following URL:
# http://fsweb.berry.edu/academic/education/vbissonnette/tables/mwu.pdf
proc initCriticalValuesTables()
{
	local numeric s1, s2;
	for s1=1 to MAX_TABLE_SIZE
	{
		for s2=1 to MAX_TABLE_SIZE
		{
			alpha05[s2,s1] = alpha01[s2,s1] = 0;
		}
	}
	# table for alpha = .05 (95%)
	alpha05[3,3]=0; alpha05[3,4]=0; alpha05[3,5]=0; alpha05[3,6]=1; alpha05[3,7]=1; alpha05[3,8]=2; alpha05[3,9]=2; alpha05[3,10]=3; alpha05[3,11]=3; alpha05[3,12]=4; alpha05[3,13]=4; alpha05[3,14]=5; alpha05[3,15]=5; alpha05[3,16]=6; alpha05[3,17]=6; alpha05[3,18]=7; alpha05[3,19]=7; alpha05[3,20]=8;
	alpha05[4,3]=0; alpha05[4,4]=0; alpha05[4,5]=1; alpha05[4,6]=2; alpha05[4,7]=3; alpha05[4,8]=4; alpha05[4,9]=4; alpha05[4,10]=5; alpha05[4,11]=6; alpha05[4,12]=7; alpha05[4,13]=8; alpha05[4,14]=9; alpha05[4,15]=10; alpha05[4,16]=11; alpha05[4,17]=11; alpha05[4,18]=12; alpha05[4,19]=13; alpha05[4,20]=14;
	alpha05[5,3]=0; alpha05[5,4]=1; alpha05[5,5]=2; alpha05[5,6]=3; alpha05[5,7]=5; alpha05[5,8]=6; alpha05[5,9]=7; alpha05[5,10]=8; alpha05[5,11]=9; alpha05[5,12]=11; alpha05[5,13]=12; alpha05[5,14]=13; alpha05[5,15]=14; alpha05[5,16]=15; alpha05[5,17]=17; alpha05[5,18]=18; alpha05[5,19]=19; alpha05[5,20]=20;
	alpha05[6,3]=1; alpha05[6,4]=2; alpha05[6,5]=3; alpha05[6,6]=5; alpha05[6,7]=6; alpha05[6,8]=8; alpha05[6,9]=10; alpha05[6,10]=11; alpha05[6,11]=13; alpha05[6,12]=14; alpha05[6,13]=16; alpha05[6,14]=17; alpha05[6,15]=19; alpha05[6,16]=21; alpha05[6,17]=22; alpha05[6,18]=24; alpha05[6,19]=25; alpha05[6,20]=27;
	alpha05[7,3]=1; alpha05[7,4]=3; alpha05[7,5]=5; alpha05[7,6]=6; alpha05[7,7]=8; alpha05[7,8]=10; alpha05[7,9]=12; alpha05[7,10]=14; alpha05[7,11]=16; alpha05[7,12]=18; alpha05[7,13]=20; alpha05[7,14]=22; alpha05[7,15]=24; alpha05[7,16]=26; alpha05[7,17]=28; alpha05[7,18]=30; alpha05[7,19]=32; alpha05[7,20]=34;
	alpha05[8,3]=2; alpha05[8,4]=4; alpha05[8,5]=6; alpha05[8,6]=8; alpha05[8,7]=10; alpha05[8,8]=13; alpha05[8,9]=15; alpha05[8,10]=17; alpha05[8,11]=19; alpha05[8,12]=22; alpha05[8,13]=24; alpha05[8,14]=26; alpha05[8,15]=29; alpha05[8,16]=31; alpha05[8,17]=34; alpha05[8,18]=36; alpha05[8,19]=38; alpha05[8,20]=41;
	alpha05[9,3]=2; alpha05[9,4]=4; alpha05[9,5]=7; alpha05[9,6]=10; alpha05[9,7]=12; alpha05[9,8]=15; alpha05[9,9]=17; alpha05[9,10]=20; alpha05[9,11]=23; alpha05[9,12]=26; alpha05[9,13]=28; alpha05[9,14]=31; alpha05[9,15]=34; alpha05[9,16]=37; alpha05[9,17]=39; alpha05[9,18]=42; alpha05[9,19]=45; alpha05[9,20]=48;
	alpha05[10,3]=3; alpha05[10,4]=5; alpha05[10,5]=8; alpha05[10,6]=11; alpha05[10,7]=14; alpha05[10,8]=17; alpha05[10,9]=20; alpha05[10,10]=23; alpha05[10,11]=26; alpha05[10,12]=29; alpha05[10,13]=33; alpha05[10,14]=36; alpha05[10,15]=39; alpha05[10,16]=42; alpha05[10,17]=45; alpha05[10,18]=48; alpha05[10,19]=52; alpha05[10,20]=55;
	alpha05[11,3]=3; alpha05[11,4]=6; alpha05[11,5]=9; alpha05[11,6]=13; alpha05[11,7]=16; alpha05[11,8]=19; alpha05[11,9]=23; alpha05[11,10]=26; alpha05[11,11]=30; alpha05[11,12]=33; alpha05[11,13]=37; alpha05[11,14]=40; alpha05[11,15]=44; alpha05[11,16]=47; alpha05[11,17]=51; alpha05[11,18]=55; alpha05[11,19]=58; alpha05[11,20]=62;
	alpha05[12,3]=4; alpha05[12,4]=7; alpha05[12,5]=11; alpha05[12,6]=14; alpha05[12,7]=18; alpha05[12,8]=22; alpha05[12,9]=26; alpha05[12,10]=29; alpha05[12,11]=33; alpha05[12,12]=37; alpha05[12,13]=41; alpha05[12,14]=45; alpha05[12,15]=49; alpha05[12,16]=53; alpha05[12,17]=57; alpha05[12,18]=61; alpha05[12,19]=65; alpha05[12,20]=69;
	alpha05[13,3]=4; alpha05[13,4]=8; alpha05[13,5]=12; alpha05[13,6]=16; alpha05[13,7]=20; alpha05[13,8]=24; alpha05[13,9]=28; alpha05[13,10]=33; alpha05[13,11]=37; alpha05[13,12]=41; alpha05[13,13]=45; alpha05[13,14]=50; alpha05[13,15]=54; alpha05[13,16]=59; alpha05[13,17]=63; alpha05[13,18]=67; alpha05[13,19]=72; alpha05[13,20]=76;
	alpha05[14,3]=5; alpha05[14,4]=9; alpha05[14,5]=13; alpha05[14,6]=17; alpha05[14,7]=22; alpha05[14,8]=26; alpha05[14,9]=31; alpha05[14,10]=36; alpha05[14,11]=40; alpha05[14,12]=45; alpha05[14,13]=50; alpha05[14,14]=55; alpha05[14,15]=59; alpha05[14,16]=64; alpha05[14,17]=67; alpha05[14,18]=74; alpha05[14,19]=78; alpha05[14,20]=83;
	alpha05[15,3]=5; alpha05[15,4]=10; alpha05[15,5]=14; alpha05[15,6]=19; alpha05[15,7]=24; alpha05[15,8]=29; alpha05[15,9]=34; alpha05[15,10]=39; alpha05[15,11]=44; alpha05[15,12]=49; alpha05[15,13]=54; alpha05[15,14]=59; alpha05[15,15]=64; alpha05[15,16]=70; alpha05[15,17]=75; alpha05[15,18]=80; alpha05[15,19]=85; alpha05[15,20]=90;
	alpha05[16,3]=6; alpha05[16,4]=11; alpha05[16,5]=15; alpha05[16,6]=21; alpha05[16,7]=26; alpha05[16,8]=31; alpha05[16,9]=37; alpha05[16,10]=42; alpha05[16,11]=47; alpha05[16,12]=53; alpha05[16,13]=59; alpha05[16,14]=64; alpha05[16,15]=70; alpha05[16,16]=75; alpha05[16,17]=81; alpha05[16,18]=86; alpha05[16,19]=92; alpha05[16,20]=98;
	alpha05[17,3]=6; alpha05[17,4]=11; alpha05[17,5]=17; alpha05[17,6]=22; alpha05[17,7]=28; alpha05[17,8]=34; alpha05[17,9]=39; alpha05[17,10]=45; alpha05[17,11]=51; alpha05[17,12]=57; alpha05[17,13]=63; alpha05[17,14]=67; alpha05[17,15]=75; alpha05[17,16]=81; alpha05[17,17]=87; alpha05[17,18]=93; alpha05[17,19]=99; alpha05[17,20]=105;
	alpha05[18,3]=7; alpha05[18,4]=12; alpha05[18,5]=18; alpha05[18,6]=24; alpha05[18,7]=30; alpha05[18,8]=36; alpha05[18,9]=42; alpha05[18,10]=48; alpha05[18,11]=55; alpha05[18,12]=61; alpha05[18,13]=67; alpha05[18,14]=74; alpha05[18,15]=80; alpha05[18,16]=86; alpha05[18,17]=93; alpha05[18,18]=99; alpha05[18,19]=106; alpha05[18,20]=112;
	alpha05[19,3]=7; alpha05[19,4]=13; alpha05[19,5]=19; alpha05[19,6]=25; alpha05[19,7]=32; alpha05[19,8]=38; alpha05[19,9]=45; alpha05[19,10]=52; alpha05[19,11]=58; alpha05[19,12]=65; alpha05[19,13]=72; alpha05[19,14]=78; alpha05[19,15]=85; alpha05[19,16]=92; alpha05[19,17]=99; alpha05[19,18]=106; alpha05[19,19]=113; alpha05[19,20]=119;
	alpha05[20,3]=8; alpha05[20,4]=14; alpha05[20,5]=20; alpha05[20,6]=27; alpha05[20,7]=34; alpha05[20,8]=41; alpha05[20,9]=48; alpha05[20,10]=55; alpha05[20,11]=62; alpha05[20,12]=69; alpha05[20,13]=76; alpha05[20,14]=83; alpha05[20,15]=90; alpha05[20,16]=98; alpha05[20,17]=105; alpha05[20,18]=112; alpha05[20,19]=119; alpha05[20,20]=127;
	# table for alpha = .01 (99%)
	alpha01[3,3]=0; alpha01[3,4]=0; alpha01[3,5]=0; alpha01[3,6]=0; alpha01[3,7]=0; alpha01[3,8]=0; alpha01[3,9]=0; alpha01[3,10]=0; alpha01[3,11]=0; alpha01[3,12]=1; alpha01[3,13]=1; alpha01[3,14]=1; alpha01[3,15]=2; alpha01[3,16]=2; alpha01[3,17]=2; alpha01[3,18]=2; alpha01[3,19]=3; alpha01[3,20]=3;
	alpha01[4,3]=0; alpha01[4,4]=0; alpha01[4,5]=0; alpha01[4,6]=0; alpha01[4,7]=0; alpha01[4,8]=1; alpha01[4,9]=1; alpha01[4,10]=2; alpha01[4,11]=2; alpha01[4,12]=3; alpha01[4,13]=3; alpha01[4,14]=4; alpha01[4,15]=5; alpha01[4,16]=5; alpha01[4,17]=6; alpha01[4,18]=6; alpha01[4,19]=7; alpha01[4,20]=8;
	alpha01[5,3]=0; alpha01[5,4]=0; alpha01[5,5]=0; alpha01[5,6]=1; alpha01[5,7]=1; alpha01[5,8]=2; alpha01[5,9]=3; alpha01[5,10]=4; alpha01[5,11]=5; alpha01[5,12]=6; alpha01[5,13]=7; alpha01[5,14]=7; alpha01[5,15]=8; alpha01[5,16]=9; alpha01[5,17]=10; alpha01[5,18]=11; alpha01[5,19]=12; alpha01[5,20]=13;
	alpha01[6,3]=0; alpha01[6,4]=0; alpha01[6,5]=1; alpha01[6,6]=2; alpha01[6,7]=3; alpha01[6,8]=4; alpha01[6,9]=5; alpha01[6,10]=6; alpha01[6,11]=7; alpha01[6,12]=9; alpha01[6,13]=10; alpha01[6,14]=11; alpha01[6,15]=12; alpha01[6,16]=13; alpha01[6,17]=15; alpha01[6,18]=16; alpha01[6,19]=17; alpha01[6,20]=18;
	alpha01[7,3]=0; alpha01[7,4]=0; alpha01[7,5]=1; alpha01[7,6]=3; alpha01[7,7]=4; alpha01[7,8]=6; alpha01[7,9]=7; alpha01[7,10]=9; alpha01[7,11]=10; alpha01[7,12]=12; alpha01[7,13]=13; alpha01[7,14]=15; alpha01[7,15]=16; alpha01[7,16]=18; alpha01[7,17]=19; alpha01[7,18]=21; alpha01[7,19]=22; alpha01[7,20]=24;
	alpha01[8,3]=0; alpha01[8,4]=1; alpha01[8,5]=2; alpha01[8,6]=4; alpha01[8,7]=6; alpha01[8,8]=7; alpha01[8,9]=9; alpha01[8,10]=11; alpha01[8,11]=13; alpha01[8,12]=15; alpha01[8,13]=17; alpha01[8,14]=18; alpha01[8,15]=20; alpha01[8,16]=22; alpha01[8,17]=24; alpha01[8,18]=26; alpha01[8,19]=28; alpha01[8,20]=30;
	alpha01[9,3]=0; alpha01[9,4]=1; alpha01[9,5]=3; alpha01[9,6]=5; alpha01[9,7]=7; alpha01[9,8]=9; alpha01[9,9]=11; alpha01[9,10]=13; alpha01[9,11]=16; alpha01[9,12]=18; alpha01[9,13]=20; alpha01[9,14]=22; alpha01[9,15]=24; alpha01[9,16]=27; alpha01[9,17]=29; alpha01[9,18]=31; alpha01[9,19]=33; alpha01[9,20]=36;
	alpha01[10,3]=0; alpha01[10,4]=2; alpha01[10,5]=4; alpha01[10,6]=6; alpha01[10,7]=9; alpha01[10,8]=11; alpha01[10,9]=13; alpha01[10,10]=16; alpha01[10,11]=18; alpha01[10,12]=21; alpha01[10,13]=24; alpha01[10,14]=26; alpha01[10,15]=29; alpha01[10,16]=31; alpha01[10,17]=34; alpha01[10,18]=37; alpha01[10,19]=39; alpha01[10,20]=42;
	alpha01[11,3]=0; alpha01[11,4]=2; alpha01[11,5]=5; alpha01[11,6]=7; alpha01[11,7]=10; alpha01[11,8]=13; alpha01[11,9]=16; alpha01[11,10]=18; alpha01[11,11]=21; alpha01[11,12]=24; alpha01[11,13]=27; alpha01[11,14]=30; alpha01[11,15]=33; alpha01[11,16]=36; alpha01[11,17]=39; alpha01[11,18]=42; alpha01[11,19]=45; alpha01[11,20]=48;
	alpha01[12,3]=1; alpha01[12,4]=3; alpha01[12,5]=6; alpha01[12,6]=9; alpha01[12,7]=12; alpha01[12,8]=15; alpha01[12,9]=18; alpha01[12,10]=21; alpha01[12,11]=24; alpha01[12,12]=27; alpha01[12,13]=31; alpha01[12,14]=34; alpha01[12,15]=37; alpha01[12,16]=41; alpha01[12,17]=44; alpha01[12,18]=47; alpha01[12,19]=51; alpha01[12,20]=54;
	alpha01[13,3]=1; alpha01[13,4]=3; alpha01[13,5]=7; alpha01[13,6]=10; alpha01[13,7]=13; alpha01[13,8]=17; alpha01[13,9]=20; alpha01[13,10]=24; alpha01[13,11]=27; alpha01[13,12]=31; alpha01[13,13]=34; alpha01[13,14]=38; alpha01[13,15]=42; alpha01[13,16]=45; alpha01[13,17]=49; alpha01[13,18]=53; alpha01[13,19]=56; alpha01[13,20]=60;
	alpha01[14,3]=1; alpha01[14,4]=4; alpha01[14,5]=7; alpha01[14,6]=11; alpha01[14,7]=15; alpha01[14,8]=18; alpha01[14,9]=22; alpha01[14,10]=26; alpha01[14,11]=30; alpha01[14,12]=34; alpha01[14,13]=38; alpha01[14,14]=42; alpha01[14,15]=46; alpha01[14,16]=50; alpha01[14,17]=54; alpha01[14,18]=58; alpha01[14,19]=63; alpha01[14,20]=67;
	alpha01[15,3]=2; alpha01[15,4]=5; alpha01[15,5]=8; alpha01[15,6]=12; alpha01[15,7]=16; alpha01[15,8]=20; alpha01[15,9]=24; alpha01[15,10]=29; alpha01[15,11]=33; alpha01[15,12]=37; alpha01[15,13]=42; alpha01[15,14]=46; alpha01[15,15]=51; alpha01[15,16]=55; alpha01[15,17]=60; alpha01[15,18]=64; alpha01[15,19]=69; alpha01[15,20]=73;
	alpha01[16,3]=2; alpha01[16,4]=5; alpha01[16,5]=9; alpha01[16,6]=13; alpha01[16,7]=18; alpha01[16,8]=22; alpha01[16,9]=27; alpha01[16,10]=31; alpha01[16,11]=36; alpha01[16,12]=41; alpha01[16,13]=45; alpha01[16,14]=50; alpha01[16,15]=55; alpha01[16,16]=60; alpha01[16,17]=65; alpha01[16,18]=70; alpha01[16,19]=74; alpha01[16,20]=79;
	alpha01[17,3]=2; alpha01[17,4]=6; alpha01[17,5]=10; alpha01[17,6]=15; alpha01[17,7]=19; alpha01[17,8]=24; alpha01[17,9]=29; alpha01[17,10]=34; alpha01[17,11]=39; alpha01[17,12]=44; alpha01[17,13]=49; alpha01[17,14]=54; alpha01[17,15]=60; alpha01[17,16]=65; alpha01[17,17]=70; alpha01[17,18]=75; alpha01[17,19]=81; alpha01[17,20]=86;
	alpha01[18,3]=2; alpha01[18,4]=6; alpha01[18,5]=11; alpha01[18,6]=16; alpha01[18,7]=21; alpha01[18,8]=26; alpha01[18,9]=31; alpha01[18,10]=37; alpha01[18,11]=42; alpha01[18,12]=47; alpha01[18,13]=53; alpha01[18,14]=58; alpha01[18,15]=64; alpha01[18,16]=70; alpha01[18,17]=75; alpha01[18,18]=81; alpha01[18,19]=87; alpha01[18,20]=92;
	alpha01[19,3]=3; alpha01[19,4]=7; alpha01[19,5]=12; alpha01[19,6]=17; alpha01[19,7]=22; alpha01[19,8]=28; alpha01[19,9]=33; alpha01[19,10]=39; alpha01[19,11]=45; alpha01[19,12]=51; alpha01[19,13]=56; alpha01[19,14]=63; alpha01[19,15]=69; alpha01[19,16]=74; alpha01[19,17]=81; alpha01[19,18]=87; alpha01[19,19]=93; alpha01[19,20]=99;
	alpha01[20,3]=3; alpha01[20,4]=8; alpha01[20,5]=13; alpha01[20,6]=18; alpha01[20,7]=24; alpha01[20,8]=30; alpha01[20,9]=36; alpha01[20,10]=42; alpha01[20,11]=48; alpha01[20,12]=54; alpha01[20,13]=60; alpha01[20,14]=67; alpha01[20,15]=73; alpha01[20,16]=79; alpha01[20,17]=86; alpha01[20,18]=92; alpha01[20,19]=99; alpha01[20,20]=105;
}
# Determine if the specified layer is a raster layer
func isRasterLayer(class GRE_LAYER l)
{
	return (l.Type == "Raster");
}
# Update a record in the database table given the recordNum, fieldName, and value to write
func updateRecord(class Vector V, string tableType, string tableName, numeric recordNum, string fieldname, numeric value)
{
	if(tableType=="point")
	{
		V.point.(tableName)[@recordNum].(fieldname) = value;
		return 1;
	}
	else if(tableType=="line")
	{
		V.line.(tableName)[@recordNum].(fieldname) = value;
		return 1;
	}
	else if(tableType=="poly")
	{
		V.line.(tableName)[@recordNum].(fieldname) = value;
		return 1;
	}
	else return 0;
}
# Write raster statistics to the database table
proc writeStatisticsToTable(numeric recordNum, string rastNameStr, numeric u, numeric z, numeric sig)
{
	local class string rastName = rastNameStr;
	rastName = rastName.slice(0,11);
	updateRecord(pointVector, "point", "ResultTable", recordNum, rastName + "_U", u);
	updateRecord(pointVector, "point", "ResultTable", recordNum, rastName + "_Z", z);
	updateRecord(pointVector, "point", "ResultTable", recordNum, rastName + "_sig", sig);
}
# Write a new record and attach it to the element specified by 'pointNum'
func writePointRecord(numeric pointNum, numeric length, numeric width)
{
	local array numeric records[1];
	records[1] = TableWriteRecord(pointTable, 0, pointNum, length, width);
	TableWriteAttachment(pointTable, pointNum, records, 1, "point");
	return records[1];	# the newly created record number
}
# Write a new record and attach it to the element specified by 'polyNum'
proc writePolyRecord(numeric length, numeric width)
{
	local array numeric records[1];
	records[1] = TableWriteRecord(polyTable, 0, length, width);
	local numeric polyNum;
	for (polyNum=1; polyNum<=NumVectorPolys(polyVector); polyNum++)
	{
		TableWriteAttachment(polyTable, polyNum, records, 1, "polygon");
	}
}
# Determine if the computed value is statistically significant or not
# return 1 if significant, 0 otherwise
func computeSignificance(numeric z, numeric u)
{
	if (n2 <= 20)
	{
		if (n1==0 && n2==0) return 0;
		if (n1==0 || n2==0) return 1;
		local numeric criticalU=0;
		if (ALPHA == .05)
		{
			criticalU = alpha05[n2,n1];
		}
		else if (ALPHA == .01)
		{
			criticalU = alpha01[n2,n1];
		}
		else
		{
			PopupMessage("Insufficient information to perform significance test");
		}
		if (u > criticalU) return 1;
		return 0;
	}
	else
	{
		if (ALPHA == .05)
		{
			return (z > 1.96);
		}
		else if (ALPHA == .01)
		{
			return (z > 2.575);
		}
		else
		{
			PopupMessage("Insufficient information to perform significance test");
		}
	}
}
# Calculate Z based on the U value
# For large samples the normal approximation z = (U - mU)/oU can be used where
# mU and oU are the mean and standard deviation of U as given by:
# mU = n1n2/2 and oU = sqrt(n1n2(n1+n2+1)/12)
func calculateZScore(numeric U, numeric n1, numeric n2)
{
	local numeric mU = n1*n2/2;
	local numeric oU = sqrt(n1*n2*(n1+n2+1)/12);
	return abs((U - mU)/oU);
}
# Get minimum of two numbers
func min(numeric a, numeric b)
{
	if (a<b) return a;
	return b;
}
# Calculate U value for designated raster (rast).
# The two sample sets are designated based on a cell's inclusion in a region.
#
# U = n1*n2 + n1(n1+1)/2 - R1
# Where n1 and n2 are the two sample sizes,
# and R1 is the sum of the ranks in sample 1
# Sample 1 is taken to be the smaller of the two groups.
func calculateUValue()
{
	n1=0; n2=0;
	local numeric R1=0, R2=0;
	local numeric U=0;
	# get sample sizes for array declarations
	foreach rast in regionIn 
	{
		if(!IsNull(rast)) n1++;
	}
	foreach rast in regionOut
	{
		if(!IsNull(rast)) n2++;
	}
	# declare arrays for samples
	local numeric size = n1 + n2;
	local array numeric mergedArray[size];
	local array numeric rank[size];
	local array numeric bitset[size];
	local numeric inVal = 1, outVal = 0;
	# copy regions to temp regions and flip if necessary
	local class REGION2D myRegIn, myRegOut;
	if (n1 <= n2)
	{
		myRegIn = CopyRegion(regionIn);
		myRegOut = CopyRegion(regionOut);
	}
	else
	{
		myRegIn = CopyRegion(regionOut);
		myRegOut = CopyRegion(regionIn);
		local numeric tmpN = n2;
		n2 = n1;
		n1 = tmpN;
	}
	# copy samples to arrays
	local numeric i=1;
	foreach rast in myRegIn
	{
		if(!IsNull(rast))
		{
			mergedArray[i] = rast;
			rank[i] = i;
			bitset[i] = inVal;
			i++;
		}
	}
	i=1;
	foreach rast in myRegOut
	{
		if(!IsNull(rast))
		{
			mergedArray[i+n1] = rast;
			rank[i+n1] = i+n1;
			bitset[i+n1] = outVal;
			i++;
		}
	}
	# rank values in mergedArray - do a shellsort
	local numeric h=1, first=1, last=n1+n2;
	while ((h * 3 + 1) < last-1)
	{
		h = 3 * h + 1;
	}
	# do the sort
	while (h > 0)
	{
		# for each of the h sets of elements
		for i = h-1 to last
		{
			local numeric key = mergedArray[i];
			local numeric bkey = bitset[i];
			local numeric j = i;
			while (j>=h && mergedArray[j-h] > key)
			{
				mergedArray[j] = mergedArray[j-h];
				bitset[j] = bitset[j-h];
				j = j - h;
			}
			mergedArray[j] = key;
			bitset[j] = bkey;
		}
		h = floor(h/3);
	}
	# Generate ranks
	local numeric next;
	for i=1 to last-1
	{
		next = i;
		# look for ties in set and resolve all at once
		local numeric sum=rank[next], count=1;
		while(next<last && mergedArray[next] == mergedArray[next+1])
		{
			sum = sum + rank[next+1];
			count++;
			next++;
		}
		local numeric j;
		for j=i to next
		{
			rank[j] = sum / count;	# use the average rank in case of tie
		}
		i = next;
	}
	# Calculate the sum of ranks for the smaller sample
	for i=1 to last
	{
		if (bitset[i]==inVal) R1 = R1 + rank[i];
		if (bitset[i]==outVal) R2 = R2 + rank[i];
	}
	# Calculate the u value
	local numeric U1=0, U2=0;
	U1 = n1*n2 + n1*(n1+1)/2 - R1;
	U2 = n1*n2 + n2*(n2+1)/2 - R2;
	U = min(U1,U2);
	return U;
}
# perform Mann-Whitney U test on all rasters in the active group
func doMannWhitneyUTest(numeric pointNum, numeric latestRecord)
{
	local class GRE_LAYER currentRaster = activegroup.FirstLayer;
	local numeric uValue = 0;
	# loop over each raster and perform calculations
	while (currentRaster != 0)
	{
		if (isRasterLayer(currentRaster))
		{
			DispGetRasterFromLayer(rast, currentRaster);
			uValue = calculateUValue();
			# calculate the z-score
			local numeric zScore = calculateZScore(uValue, n1, n2);
			# compute whether test result is statistically significant
			local numeric signif = computeSignificance(zScore, uValue);
			# write records to the database
			writeStatisticsToTable(latestRecord, rast.$INFO.Name, uValue, zScore, signif);
		}
		currentRaster = currentRaster.NextLayer;
	}
}
# Checks layer to see if it is valid.
func checkLayer()
{
	local numeric valid = true;
	# Get names layers if usable.  If not output error messages.
	# Get name of active layer if it is usable.  If not output an error message.
	if (activegroup.ActiveLayer.Type == "")
	{
		PopupMessage("Group has no layers!");
		valid = false;
	}
	else if (activegroup.ActiveLayer.Type == "Vector")
	{
		vectorLayer = activegroup.ActiveLayer;
		DispGetVectorFromLayer(lineVector, vectorLayer);
		if (lineVector.$Info.NumLines < 1)
		{
			PopupMessage("No lines!  Please make the 'boundary' vector the active layer");
			valid = false;
		}
	}
	else
	{
		PopupMessage("Not a vector!  Please make the 'boundary' vector the active layer");
		valid = false;
	}
	return valid;
}
# find the closest line to 'srcPoint' in the lineVector.
# @return 0 if a line is not found within the specified snapDistance.
func getClosestLine(class Vector v, class POINT2D srcPoint, snapDistance)
{
	local class Georef georef = GetLastUsedGeorefObject(v);
	local class POINT2D retPoint, tmpPoint;
	local numeric lineNumber = 0;
	if (v.$Info.NumLines > 0)
	{
		lineNumber = FindClosestLine(v, srcPoint.x, srcPoint.y, georef, snapDistance);
	}
	else
	{
		PopupMessage("Selected vector does not have any lines");
	}
	if (lineNumber==0) 	# check for error in finding line
	{
		PopupMessage("Cannot snap the specified point to line given existing snap distance");
	}
	return lineNumber;
}
# Takes user selected point and finds a point on the nearest line to snap to
func class POINT2D snapToLine(class POINT2D srcPoint, numeric snapDistance)
{
	local class POINT2D retPoint, tmpPoint;
	local numeric lineNum = getClosestLine(lineVector, srcPoint, snapDistance);
	if (lineNum==0) 	# check for error in finding line
	{
		PopupMessage("Cannot snap the specified point to line given set snap distance");
	}
	else	# no error so find the closest point to snap to
	{
		local class POLYLINE polyline = GetVectorLine(lineVector, lineNum);
		if (polyline == 0)
		{
			PopupMessage("Cannot snap the specified point to line given set snap distance");
		}
		else
		{
			local class Georef georef = GetLastUsedGeorefObject(lineVector);
			tmpPoint = 	MapToObject(georef, srcPoint.x, srcPoint.y, lineVector);
			retPoint = polyline.FindClosestPoint(tmpPoint);
			retPoint = ObjectToMap(lineVector, retPoint.x, retPoint.y, georef);
		}
	}
	return retPoint;
}
# compute the slope between two points (for defining the rectangle)
func computeSlope(class POINT2D point1, class POINT2D point2)
{
	if (point2.x==point1.x) return infinity;
	return (point2.y - point1.y) / (point2.x - point1.x);
}
# create the rectangle as specified by the corner points
proc addRectangle(class Vector destVector, class POINT2D ll, class POINT2D ul, class POINT2D ur, class POINT2D lr)
{
	local array numeric x[5];
	local array numeric y[5];
	x[1] = ll.x;
	y[1] = ll.y;
	x[2] = ul.x;
	y[2] = ul.y;
	x[3] = ur.x;
	y[3] = ur.y;
	x[4] = lr.x;
	y[4] = lr.y;
	x[5] = ll.x;
	y[5] = ll.y;
	VectorAddLine(destVector, 5, x, y);
#	refreshVector();
}
# Compute the slope of the polyline near the centerpoint
func computeLineSlope(class Vector destVector, class POINT2D centerPoint, numeric lineNum)
{
	# Get the line as a polyline
	local class POLYLINE polyline = GetVectorLine(lineVector, lineNum);
	# Set up the vertices for slope computation (first, last, selected)
	local class Georef georef = GetLastUsedGeorefObject(lineVector);
	local class POINT2D objCenterPoint = MapToObject(georef, centerPoint.x, centerPoint.y, lineVector);
	local numeric vertexNum = polyline.FindClosestVertex(objCenterPoint);
	local numeric first = 0, last = polyline.GetNumPoints() - 1;
	# set up vertices for computation
	if (vertexNum == first) # if vertex is first in line use first two vertices 
	{
		last = first + 1;
	}
	else if (vertexNum == last) # if vertex is last in line use last two vertices 
	{
		first = last - 1;
	}
	else # vertex isn't the first or last, use adjacent vertices for computation
	{
		first = vertexNum - 1;
		last = vertexNum + 1;
	}
	# Get the vertices and convert to map coords
	local class POINT2D pointFirst, pointLast;
	pointFirst = polyline.GetVertex(first);
	pointFirst = ObjectToMap(lineVector, pointFirst.x, pointFirst.y, georef);
	pointLast = polyline.GetVertex(last);
	pointLast = ObjectToMap(lineVector, pointLast.x, pointLast.y, georef);
	# compute the slope
	return computeSlope(pointFirst, pointLast);
}
# Creates a rectangle poly in 'destVector' with the slope of the line at linenum.
# The rectangle is centered at 'centerPoint' with dimensions length x width.
proc createRectangle(class Vector destVector, numeric length, numeric width, class POINT2D centerPoint, numeric lineNum)
{
	local numeric slope = computeLineSlope(destVector, centerPoint, lineNum);
	# handle the orientation (slope) of the rectangle about the point
	local class POINT2D tmp, ul, ur, ll, lr;
	numeric theta = atand(slope);
	tmp.x = centerPoint.x - .5*width*cosd(theta);
	tmp.y = centerPoint.y - .5*width*sind(theta);
	# get lower left
	ll.x = tmp.x + .5*length*cosd(90 - theta);
	ll.y = tmp.y - .5*length*sind(90 - theta);
	# get upper left
	ul.x = tmp.x - .5*length*cosd(90 - theta);
	ul.y = tmp.y + .5*length*sind(90 - theta);
	tmp.x = centerPoint.x + .5*width*cosd(theta);
	tmp.y = centerPoint.y + .5*width*sind(theta);
	# get lower right
	lr.x = tmp.x + .5*length*cosd(90 - theta);
	lr.y = tmp.y - .5*length*sind(90 - theta);
	# get upper right
	ur.x = tmp.x - .5*length*cosd(90 - theta);
	ur.y = tmp.y + .5*length*sind(90 - theta);
	addRectangle(destVector, ll, ul, ur, lr);
}
# create the line given adding it to the destVector
proc createLine(class Vector destVector, class POLYLINE line)
{
	numeric numPoints = line.GetNumPoints();
	local array numeric xcoords[numPoints];
	local array numeric ycoords[numPoints];
	local class POINT2D vertex;
	local class Georef georef = GetLastUsedGeorefObject(lineVector);
	local numeric i, x, y;
	for (i=1; i<=numPoints; i++)
	{
		vertex = line.GetVertex(i-1);
		ObjectToMap(lineVector, vertex.x, vertex.y, georef, x, y);
		xcoords[i] = x;
		ycoords[i] = y;
	}
# bug in scope?  this is cleared to 0
numPoints = line.GetNumPoints();
	VectorAddLine(destVector, numPoints, xcoords, ycoords);
}
# Get the snap distance setting
func getSnapDistance()
{
	local class GUI_CTRL_EDIT_NUMBER snapCtrl = dlgwin.GetCtrlByID("snap");
	return snapCtrl.GetValue();
}
# Called when user presses 'left' pointer/mouse button. 
func OnLeftButtonPress()
{ 
	# If the selected layer is not valid, don't do anything.
	if (checkLayer())
	{
		# Set local variables
		local class POINT2D point;
		# Check point.
		point.x = PointerX;
		point.y = PointerY;
		point = TransPoint2D(point, ViewGetTransViewToScreen(View, 1));
		point = TransPoint2D(point, ViewGetTransMapToView(View, vectorLayer.Projection, 1));
		local numeric snapDistance = getSnapDistance();
		point = snapToLine(point, snapDistance);
		# Add the point to the target vector
		if (point.x !=0 && point.y !=0)
		{
			VectorAddPoint(pointVector, point.x, point.y);
#			pointLayer = activegroup.GetLayerByName(pointLayerName);
#			if (pointLayer==0) PopupMessage("null point layer "+ pointLayerName);
#			else pointLayer.UpdateExtents();
			View.Redraw(0);	# if using 7.0 only redraw point and poly layers
		}
	}
}
# Creates the vector that will store the resultant points
proc createDestVector(class Vector destVector)
{
	local string flags$ = "Polygonal";
	GetOutputVector(destVector, flags$);
	local class Georef georef = GetLastUsedGeorefObject(lineVector);
	if (georef==0) PopupMessage("No georef");
	# Create Implied georeference for output vector based on lineVector's projection
	else
	{
		DeleteGeoref(destVector);
		CreateImpliedGeoref(destVector, georef.Projection);
	}
	VectorToolkitInit(destVector);
}
# Creates the vector that will store the resultant points
proc createTmpDestVector(class Vector destVector)
{
	local string flags$ = "Polygonal";
	CreateTempVector(destVector, flags$);
	local class Georef georef = GetLastUsedGeorefObject(lineVector);
	if (georef==0) PopupMessage("No georef");
	# Create Implied georeference for output vector based on lineVector's projection
	else
	{
		DeleteGeoref(destVector);
		CreateImpliedGeoref(destVector, georef.Projection);
	}
	VectorToolkitInit(destVector);
}
# Called after test is run - new vector must then be used
proc ClosePolyVector()
{
	CloseVector(polyVector);
	CloseVector(tmpVector);
	polyCtrl.SetValueStr("");
}
# Set the alpha value based on the dialog's settings
proc setAlphaValue()
{
	local class GUI_FORM_RADIOGROUP alphaRadio = dlgwin.GetCtrlByID("alpha");
	if (alphaRadio.GetSelected()=="alpha01") ALPHA = .01;
	else ALPHA = .05;
}
# Utility function to bound val between two limits
func bound(numeric val, numeric lowerLimit, numeric upperLimit)
{
	if (val < lowerLimit) return lowerLimit;
	if (val > upperLimit) return upperLimit;
	return val;
}
# Compute the distance between two points
func computeDistance(class POINT2D p1, class POINT2D p2)
{
	return sqrt((p2.x-p1.x)^2 + (p2.y-p1.y)^2);
}
# Function used to split the polyline at the edges of the rectangle
func class POLYLINE splitPolyline(class Vector destVector, class POINT2D centerPoint, numeric width, class POLYLINE polyline, numeric lineNum)
{
	local numeric slope = computeLineSlope(destVector, centerPoint, lineNum);
	# handle the orientation (slope) of the rectangle about the point
	local class POINT2D left, right;
	numeric theta = atand(slope);
	# Get the georef so we can work in obj coords as polyline does
	local class Georef georef = GetLastUsedGeorefObject(lineVector);
	# find the left edge (not necessary left edge of rect)
	left.x = centerPoint.x - .5*width*cosd(theta);
	left.y = centerPoint.y - .5*width*sind(theta);
	local class POINT2D objLeftPoint = MapToObject(georef, left.x, left.y, lineVector);
	local numeric leftNum = polyline.FindClosestVertex(objLeftPoint);
	# find the right edge (not necessary right edge of rect)
	right.x = centerPoint.x + .5*width*cosd(theta);
	right.y = centerPoint.y + .5*width*sind(theta);
	local class POINT2D objRightPoint = MapToObject(georef, right.x, right.y, lineVector);
	local numeric rightNum = polyline.FindClosestVertex(objRightPoint);
	# swap if necessary
	if (leftNum>rightNum)
	{
		local numeric tmp = leftNum;
		leftNum = rightNum;
		rightNum = tmp;
	}
	# calculate necessary numpoints compensating for curvature
	local numeric numpoints;
	numpoints = 2 * width * polyline.GetNumPoints() / polyline.ComputeLength();
	# pad vertices past the edge
	local numeric pad = (numpoints - (rightNum - leftNum));
	if (pad<25) pad = 25; # use at least 25
	leftNum = leftNum - pad;
	rightNum = rightNum + pad;
	leftNum = bound(leftNum, 0, polyline.GetNumPoints()-1);
	rightNum = bound(rightNum, 0, polyline.GetNumPoints()-1);
	# create the polyline
	local class POLYLINE retLine;
	local numeric v=0;
	for (v=leftNum; v<=rightNum; v++)
	{
		retLine.AppendVertex(polyline.GetVertex(v));
	}
	return retLine;
}
# Get the Y scale for the current field
func getYScale(string field)
{
	if (field == "U_Value") return .5;		# max is 'max'
	else if (field == "z_score") return 25;	# max is 4 (100 scaled)
	return 50;							# max is 1	(50 scaled)
}
# Style the points by script
func string generateStyleScript(string field)
{
	local class STRINGLIST rasterLayerList, rasterFieldList;
	# loop over each raster and perform calculations
	local class GRE_LAYER currentRaster = activegroup.FirstLayer;
	while (currentRaster != 0)
	{
		if (isRasterLayer(currentRaster))
		{
			DispGetRasterFromLayer(rast, currentRaster);
			local class string name, suffix;
			if (field == "U_Value") suffix = "_U";
			else if (field == "z_score") suffix = "_Z";
			else suffix = "_sig";
			name = rast.$INFO.Name;
			rasterLayerList.AddToEnd(name);
			name = name.slice(0,11) + suffix;
			rasterFieldList.AddToEnd(name);
		}
		currentRaster = currentRaster.NextLayer;
	}
	local class string script ='
	# default colors to use for styles
	local class COLOR color;
	class STRINGLIST colors;
	colors.AddToEnd("red");
	colors.AddToEnd("green");
	colors.AddToEnd("blue");
	colors.AddToEnd("cyan");
	colors.AddToEnd("magenta");
	colors.AddToEnd("yellow");
	class STRINGLIST rasters;
	';
	local numeric r, numRasts = rasterLayerList.GetNumItems();
	for (r=0; r<numRasts; r++)
	{
		script=script + "rasters.AddToEnd(\""+rasterLayerList.GetString(r)+"\");";
	}
	script+=
	'
	# set constant values
	local numeric BAR_SCALE_FACTOR = 10;
	local numeric COLOR_SCALE_FACTOR = 255 / 100;
	local numeric X_SCALE = 1;
	local numeric Y_SCALE = ' +
	NumToStr(getYScale(field)); 
	script +=
 	';
	func class COLOR getColor(numeric colorNum)
	{
		local numeric numColors = colors.GetNumItems();
		local class COLOR c;
		c.name = colors.GetString(colorNum%numColors-1);
		return c;
	}
	# Set edge line color and cylinder dimensions
	LineStyleSetColor(0,0,0);
	numeric longAxis = 12*BAR_SCALE_FACTOR * X_SCALE, shortAxis = 5*BAR_SCALE_FACTOR * X_SCALE;
	# Set text color and font
	LineStyleSetTextColor(0,0,0);
	LineStyleSetScale(100);
	LineStyleSetFont("ARIALBD.TTF");
	# Draw three cylinders side by side
	LineStyleDropAnchor(1);
	';
	local numeric j;
	for (j=1; j<=numRasts; j++)
	{
	script = script + 
	'
	color = getColor('+NumToStr(j)+');
	local numeric red, green, blue;
	red = color.red * COLOR_SCALE_FACTOR;
	green = color.green * COLOR_SCALE_FACTOR;
	blue = color.blue * COLOR_SCALE_FACTOR;
	LineStyleDrawCylinder(longAxis,shortAxis,ResultTable['+NumToStr(currentRun)+'].'+rasterFieldList.GetString(j-1)+' * BAR_SCALE_FACTOR * Y_SCALE, red, green, blue);
	LineStyleMoveTo(0,longAxis); # move right by width of cylinder
	';
	}
	script+=
	'
	# Draw label centered below each cylinder
	LineStyleMoveToAnchor(1);	# move to base of first cylinder
	LineStyleMoveTo(-90,100);	# move down to make room for label
	# Draw all of the labels with color
	for i=1 to ' +
	NumToStr(numRasts);
	script+=
	'
	{
		color = getColor(i);
		local numeric red, green, blue;
		red = color.red * COLOR_SCALE_FACTOR;
		green = color.green * COLOR_SCALE_FACTOR;
		blue = color.blue * COLOR_SCALE_FACTOR;
		LineStyleSetTextColor(red, green, blue);
		LineStyleDrawText(rasters.GetString(i-1),70,-60,0); # to center first label
		LineStyleMoveTo(0,longAxis); # move right by width of cylinder
	}
	';
	return script;
}
# Setup the point styles for the point layer
proc setupPointStyles()
{
	pointLayer.Point.Select.Mode = "All";
	pointLayer.Point.StyleMode = "ByScript";
	local class GUI_FORM_RADIOGROUP graphRadio = dlgwin.GetCtrlByID("graphStatistic");
	pointLayer.Point.Script = generateStyleScript(graphRadio.GetSelected());
}
# Create the desired fields in the point vector table for each raster
proc createRasterLayerFields()
{
	# loop over each raster and perform calculations
	local class GRE_LAYER currentRaster = activegroup.FirstLayer;
	while (currentRaster != 0)
	{
		if (isRasterLayer(currentRaster))
		{
			DispGetRasterFromLayer(rast, currentRaster);
			class string rastName = rast.$INFO.Name;
			local class string ufield$ = rastName.slice(0,11) + "_U";
			local class string zfield$ = rastName.slice(0,11) + "_Z";
			local class string sigfield$ = rastName.slice(0,11) + "_sig";
			TableAddFieldFloat(pointTable, ufield$, 10, 2);
			TableAddFieldFloat(pointTable, zfield$, 10, 10);
			TableAddFieldInteger(pointTable, sigfield$);
		}
		currentRaster = currentRaster.NextLayer;
	}
}
# see if the record with the given id exists in the result table
func recordExists(numeric id, numeric length, numeric width)
{
	local numeric i;
	for (i=1; i<=NumRecords(pointTable); i++)
	{
		if (pointVector.point.ResultTable[@i].ID == id &&
			pointVector.point.ResultTable[@i].length == length &&
			pointVector.point.ResultTable[@i].width == width
			)
		return 1;
	}
	return 0;
}
# Add the vector to the view
func string addVectorToDisplay(class Vector v)
{
	# make sure we reset active layer
	local string layername$ = activegroup.ActiveLayer.Name;
	local class GRE_LAYER_VECTOR vlayer;
	vlayer = GroupQuickAddVectorVar(activegroup, v);
	local string retName = vlayer.Name;
	local class GRE_LAYER actlayer;
	actlayer = activegroup.GetLayerByName(layername$);
	activegroup.SetActiveLayer(actlayer);
	return retName;
}
# Procedure called when apply button is pressed
proc OnApply()
{
	if (polyCtrl.GetValueStr()=="" || pointCtrl.GetValueStr()=="")
	{
		PopupMessage("Cannot run tests due to invalid input.  Ensure that both output vectors are selected");
	}
	else
	{
		# set the alpha value (which is global) according to current setting
		setAlphaValue();
		local numeric pointCount = NumVectorPoints(pointVector);
		if (pointCount<=0)
		{
			PopupMessage("The selected vector has no points.  Please locate in the display the points of interest and then use \'Apply\'");
		}
		else
		{
			createRasterLayerFields();
			local class POINT2D point;
			local class POLYLINE polyline;
			local numeric i, snapDistance = getSnapDistance(), lineNum = 0, splitLine = 0, latestRecord=0;
			local numeric length = lengthCtrl.GetValueNum(), width = widthCtrl.GetValueNum();
			length = length * 2;	# length is defined as the length of one sample
			for (i=1; i<=NumVectorPoints(pointVector); i++)
			{
				#if (!recordExists(i, length/2, width))
				latestRecord = writePointRecord(i, length / 2, width);	# do a new record for each point
				# get a point and find its associated line
				point.x = pointVector.point.Internal[@i].x;
				point.y = pointVector.point.Internal[@i].y;
				lineNum = getClosestLine(lineVector, point, snapDistance);
				polyline = GetVectorLine(lineVector, lineNum);
				# create the rectangle and add the line to tmpVector
				createRectangle(polyVector, length, width, point, lineNum);
				createRectangle(tmpVector, length, width, point, lineNum);
				polyline = splitPolyline(tmpVector, point, width, polyline, lineNum);
				createLine(tmpVector, polyline);	# if using 7.0 replace with VectorAddPolyline() - check obj vs map coords
				# setup two regions based on split polygon
				# for now IN is left of line, OUT is region to the right
				splitLine = getClosestLine(tmpVector, point, snapDistance);
				local numeric leftpoly = tmpVector.line.Internal[@splitLine].LeftPoly;
				local numeric rightpoly = tmpVector.line.Internal[@splitLine].RightPoly;
				local class Georef tmpGeoref = GetLastUsedGeorefObject(tmpVector);
				regionIn = ConvertVectorPolyToRegion(tmpVector, leftpoly, tmpGeoref);
				regionOut = ConvertVectorPolyToRegion(tmpVector, rightpoly, tmpGeoref);
				doMannWhitneyUTest(i, latestRecord);
			}
			writePolyRecord(length / 2, width);
			ClosePolyVector();
			PopupMessage("U-Value computations are complete");
			addVectorToDisplay(polyVector);
			local string pointLayerName = addVectorToDisplay(pointVector);
			pointLayer = activegroup.GetLayerByName(pointLayerName);
			setupPointStyles();
			if (latestRecord>0) {currentRun++;}
			LayerOpenControls(pointLayer);
		}
	}
}
func OnOk()
{
	OnApply();
	return 1;
}
# Callback for when the active group changes.
proc cbGroup()
{
	activegroup = Layout.ActiveGroup;
}
# Called when tool is activated.
# If the tool implements a dialog it should be "managed" (displayed) here.
proc OnActivate ()
{
	dlgwin.SetOkEnabled(0);
	dlgwin.Open();
}
# Called when tool is deactivated (usually when switching to another tool).
# If the tool implements a dialog it should be "unmanaged" (hidden) here.
proc OnDeActivate ()
{
	dlgwin.Close(0);
#	View.SetDefaultTool();
}
# Create the desired fields in the point vector table
proc createPointTableFields()
{
	TableAddFieldInteger(pointTable, "ID");
	TableAddFieldInteger(pointTable, "length");
	TableAddFieldInteger(pointTable, "width");
}
# Create the vector point database tables as desired
proc initializePointDatabaseTable(class Vector v)
{
	local string name$ = "ResultTable";
	local string desc$ = "Computed statistics for U-value, z-value, and significance";
	local class DATABASE db = OpenVectorPointDatabase(v);
	# if the table doesn't exist it needs to be created
	if(!TableExists(db, name$))
	{
		pointTable = TableCreate(db, name$, desc$);
		pointTable.OneElementPerRecord = 1;
		createPointTableFields();
	}
	# else the table does exist, get info for it
	else
	{
		pointTable = DatabaseGetTableInfo(db, name$);
	}
}
# Create the desired fields in the polygon vector table
proc createPolygonTableFields()
{
	TableAddFieldInteger(polyTable, "Length");
	TableAddFieldInteger(polyTable, "Width");
}
# Create the vector point database tables as desired
proc initializePolygonDatabaseTable(class Vector v)
{
	local string name$ = "Boundary";
	local string desc$ = "Boundary size used for U-Value computation";
	local class DATABASE db = OpenVectorPolyDatabase(v);
	# if the table doesn't exist it needs to be created
	if(!TableExists(db, name$))
	{
		polyTable = TableCreate(db, name$, desc$);
		polyTable.OneRecordPerElement = 1;
		createPolygonTableFields();
	}
	# else the table does exist, get info for it
	else
	{
		polyTable = DatabaseGetTableInfo(db, name$);
	}
}
# Procedure called when user presses the button to add new point vector object
proc GetPointVector()
{
	if (checkLayer()) 
	{
		createDestVector(pointVector);
		initializePointDatabaseTable(pointVector);
	}
	local class FILEPATH fp = pointVector.$INFO.Filename;
	pointCtrl.SetValueStr(fp.GetName() +" / "+ pointVector.$INFO.Name);
}
# Procedure called when user presses the button to add new polygon vector object
proc GetPolyVector()
{
	createDestVector(polyVector);
	initializePolygonDatabaseTable(polyVector);
	createTmpDestVector(tmpVector);
	local class FILEPATH fp = polyVector.$INFO.Filename;
	polyCtrl.SetValueStr(fp.GetName() +" / "+ polyVector.$INFO.Name);
}
# Called the first time the tool is activated.
proc OnInitialize ()
{
	initCriticalValuesTables();
	if (Layout) {
		WidgetAddCallback(Layout.GroupSelectedCallback, cbGroup);
		activegroup = Layout.ActiveGroup;
		}
	else activegroup = Group;
	local string dialogSpecXml$ = '<?xml version="1.0" encoding="UTF-8"?>
	<!DOCTYPE root SYSTEM "smlforms.dtd">
	<root>
		<dialog id="utestdialog" Title="Boundary U-Test" OnOK="OnOk()" OnApply="OnApply()" OnCancel="OnClose()" >
			<pane Orientation="vertical">
				<groupbox Name=" Select Vector Objects: " VertResize="Fixed" ExtraBorder="4">
					<pane Orientation="horizontal">
						<pushbutton id="getpoint" Name="Point Vector..." Width="25" HorizResize="Fixed" OnPressed="GetPointVector()"/>
						<edittext id="point" Width="25" ReadOnly="true"/>
					</pane>
					<pane Orientation="horizontal">
						<pushbutton id="getpoly" Name="Poly Vector..." Width="25" HorizResize="Fixed" OnPressed="GetPolyVector()"/>
						<edittext id="poly" Width="25" ReadOnly="true"/>
					</pane>
				</groupbox>
				<groupbox Name=" Boundary Settings: " VertResize="Fixed" ExtraBorder="4">
					<pane Orientation="horizontal">
						<label Width="25" HorizResize="Fixed">Length:</label>
						<editnumber id="length" Precision="0" Default="5000" MinVal="0"/>
						<label>meters</label>
					</pane>
					<pane Orientation="horizontal">
						<label Width="25" HorizResize="Fixed">Width:</label>
						<editnumber id="width" Precision="0" Default="5000" MinVal="0"/>
						<label>meters</label>
					</pane>
					<pane Orientation="horizontal">
						<label Width="25" HorizResize="Fixed">Snap Distance:</label>
						<editnumber id="snap" Precision="0" Default="10000" MinVal="100"/>
						<label>meters</label>
					</pane>
				</groupbox>
				<groupbox Name=" Significance Settings: " VertResize="Fixed" ExtraBorder="4">
					<radiogroup id="alpha" Default="alpha05" VertResize="Fixed">
						<item Value="alpha05" Name="95% significance test"/>
						<item Value="alpha01" Name="99% significance test"/>
					</radiogroup>
				</groupbox>
				<groupbox Name=" Select Graph Statistic: " VertResize="Fixed" ExtraBorder="4">
					<radiogroup id="graphStatistic" Default="U_Value" VertResize="Fixed">
						<item Value="U_Value" Name="U Value"/>
						<item Value="z_score" Name="Z-Score"/>
						<item Value="Significance" Name="Significance"/>
					</radiogroup>
				</groupbox>
			</pane>
		</dialog>
	</root>
	';
	# Parse the xml dialog specification
	local class XMLDOC dlgdoc;
	local numeric err = dlgdoc.Parse(dialogSpecXml$);
	if (err < 0) {
		PopupError(err);
		Exit();
		}
	local string dlgid$ = "utestdialog";
	local class XMLNODE dlgnode = dlgdoc.GetElementByID(dlgid$);
	if (dlgnode == 0) {
		PopupMessage("Could not find specifed id: "+dlgid$);
		Exit();
		}
	dlgwin.SetXMLNode(dlgnode);
	dlgwin.CreateModeless(View.InfoForm);
	pointCtrl = dlgwin.GetCtrlByID("point");
	polyCtrl = dlgwin.GetCtrlByID("poly");
	widthCtrl = dlgwin.GetCtrlByID("width");
	lengthCtrl = dlgwin.GetCtrlByID("length");
}
proc OnClose()
	{
	dlgwin.Close(1);
	View.SetDefaultTool();		# switch to default tool on the View tool bar
	}