# 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 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 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; r0) {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$ = ' '; # 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 }