# 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
}