Syntax Highlighing:
comments, key words, predefined symbols, class members & methods, functions & classes
# REGSTATS.SML
# View ToolScript
# Provides a polygon tool that allows user to outline an area of a
# raster in the view and compute area statistics for the area.
# The raster must be a continuous-value type (not a composite raster)
# that is the active layer in the active group in the View. The
# raster should have a projected coordinate reference system (i.e., not
# geographic (latitude/longitude).
# The script then computes and shows in the control dialog the following
# values: the number of cells, number of non-null cells, minimum, maximum,
# mean, standard deviation, area, perimeter, centroid, and surface area
# over that region. The user may choose any distance or area units for the
# output to be displayed in.
# Requires TNTmips version 2012:78 or later.
#
# Version 26 August 2013
# Changed the function to create the polygon tool to ViewCreatePolygonGadget()
# to be consistent with internal class changes.
#
# The following symbols are predefined
# class GRE_VIEW View {use to access the view the tool script is attached to}
# class GRE_GROUP Group {use to access the group being viewed if the script is run from a group view}
# class GRE_LAYOUT Layout {use to access the layout being viewed if the script is run from a layout view}
# class GUI_GADGET ScriptGadget
# numeric ToolIsActive Will be 0 if tool is inactive or 1 if tool is active
#
# The following values are also predefined and are valid when the various On...()
# functions are called which deal with pointer and keyboard events.
# numeric PointerX Pointer X coordinate within view in pixels
# numeric PointerY Pointer Y coordinate within view in pixels
# numeric ShiftPressed 1 if <shift> key being pressed or 0 if not
# numeric CtrlPressed 1 if <ctrl> key being pressed or 0 if not
# numeric LeftButtonPressed 1 if left pointer button pressed or 0 if not
# numeric RightButtonPressed 1 if right pointer button pressed or 0 if not
# numeric MiddleButtonPressed 1 if middle pointer button pressed or 0 if not
#
# The following script functions will be called (if used in the script) when
# the appropriate action or event occurs as described in the comments before each.
# To use a function, uncomment the lines containing the 'func' definition
# and ending brace '}' by removing the leftmost '#' on the line and add the
# function code between the two lines.
### GLOBAL VARIABLES
class GRE_GROUP activeGroup; # the active group in the View
class GRE_LAYER rasterLayer; # active layer containing the target raster
class RVC_RASTER targetRaster; # target raster object
class RVC_GEOREFERENCE rastGeoref; # georeference for the target raster
class STRING rasterName$;
class STRING coordUnit$; # coordinate unit for raster map coordinate system
class GUI_GADGET_POLYLINE polyTool; # class for polyline tool in the View
numeric distScale, areaScale;
numeric min, max, mean, stdDev, count, cells, sum, sumsqr, surface, area, perimeter;
class POINT2D centroid;
numeric setDefaultWhenClose;
# variables for dialog controls
class GUI_DLG dlgwin;
class GUI_CANVAS canvas; # drawing canvas
class GUI_CTRL_COMBOBOX distUnits, areaUnits;
class GUI_CTRL_PUSHBUTTON saveAsBtn;
class GC gc; # graphics context for the drawing canvas
###################################################################################
############## USER-DEFINED FUNCTIONS AND PROCEDURES ##########################
###################################################################################
### Checks layer to see if it is valid.
func checkLayer()
{
local numeric valid = false;
# Get name of active layer if it is usable. If not output an error message.
if (activeGroup.ActiveLayer.TypeID == "")
{
rasterName$ = "Group has no layers!";
}
else if (activeGroup.ActiveLayer.TypeID == "Raster")
{
rasterLayer = activeGroup.ActiveLayer;
DispGetRasterFromLayer(targetRaster, rasterLayer);
if (targetRaster.$Info.Type != "binary" and
targetRaster.$Info.Type != "4-bit unsigned" and
targetRaster.$Info.Type != "8-bit signed" and
targetRaster.$Info.Type != "8-bit unsigned" and
targetRaster.$Info.Type != "16-bit signed" and
targetRaster.$Info.Type != "16-bit unsigned" and
targetRaster.$Info.Type != "32-bit signed" and
targetRaster.$Info.Type != "32-bit unsigned" and
targetRaster.$Info.Type != "32-bit floating-point" and
targetRaster.$Info.Type != "64-bit signed" and
targetRaster.$Info.Type != "64-bit unsigned" and
targetRaster.$Info.Type != "64-bit floating-point") then
rasterName$ = "Type not supported!";
else
{
rasterName$ = rasterLayer.Name;
rastGeoref.OpenLastUsed(targetRaster);
coordUnit$ = rastGeoref.GetCoordRefsys().CoordSys.GetAxis(1).Unit.GetSymbol();
valid = true;
}
}
else
rasterName$ = "Not a raster!";
return valid;
}
### Callback procedure to redraw the canvas.
proc cbRedraw()
{
local numeric larea, lperimeter, lsurface;
# Scale the values to specified units.
larea = areaScale * area;
lperimeter = distScale * perimeter;
lsurface = areaScale * surface;
# Create graphics context for the canvas in the control dialog
gc = canvas.CreateGC();
# Clear the drawing area and redraw text.
gc.SetColorRGB(255, 255, 255, 255);
gc.FillRect(0, 0, canvas.GetWidth(), canvas.GetHeight() );
gc.SetColorName("black");
gc.DrawTextSetFont("ARIALBD.TTF");
gc.DrawTextSetHeightPixels(12);
# print raster name
gc.DrawTextSimple("Raster:", 0, 10);
gc.DrawTextSimple(sprintf("%s", rasterName$), canvas.GetWidth() * 0.5, 10);
# print value labels
gc.DrawTextSimple(sprintf("Cells:\nNull Cells:\nMinimum:\nMaximum:\nMean:\nStandard Deviation:\nArea:\nPerimeter:\nCentroid:\nSurface Area:"), 0, 25);
if (cells > 0) # if an area of cells is outlined, print the statistics values to the canvas
{
gc.DrawTextSimple(sprintf("%d\n%d\n%.2f\n%.2f\n%.2f\n%.2f\n%.2f\n%.2f\n%.2f, %.2f\n%.2f",
count, cells - count, min, max, mean, stdDev, larea, lperimeter, centroid.x, centroid.y, lsurface), canvas.GetWidth() * 0.5, 25);
}
canvas.Refresh();
}
### Callback for when the active layer changes.
proc cbLayer()
{
checkLayer();
cbRedraw();
}
# Callback for when the active group changes.
proc cbGroup()
{
activeGroup = Layout.ActiveGroup;
WidgetAddCallback(activeGroup.LayerSelectedCallback, cbLayer);
cbLayer();
}
# Called when user clicks the right mouse button on the polygon tool
# to accept the polygon.
proc cbToolApply()
{
cells = 0; min = 0; max = 0; mean = 0; stdDev = 0; sum = 0; sumsqr = 0; count = 0; surface = 0; area = 0; perimeter = 0;
centroid.x = 0; centroid.y = 0; current = 0; right = 0; down = 0; downright = 0;
# If the selected layer is not valid, don't do anything.
if (checkLayer())
{
local numeric sum, sumsqr, xscale, yscale, zscale;
local numeric current, right, down, downright;
local class REGION2D MyRgn;
xscale = targetRaster.$Info.ColScale;
yscale = targetRaster.$Info.LinScale;
zscale = activeGroup.ActiveLayer.zscale;
# Get region and transform it to the appropriate coordinate system.
MyRgn = polyTool.Region;
MyRgn = RegionTrans(MyRgn, View.GetTransViewToScreen(1));
MyRgn = RegionTrans(MyRgn, View.GetTransMapToView(rastGeoref.GetCoordRefSys(), 1) );
# Loop on region to calculate statistics.
local numeric lin, col;
for each targetRaster[lin, col] in MyRgn {
if (!IsNull(targetRaster)) {
if (count == 0) {
max = targetRaster;
min = targetRaster;
}
else if (targetRaster > max) {
max = targetRaster;
}
else if (targetRaster < min) {
min = targetRaster;
}
sum += targetRaster;
sumsqr += sqr(targetRaster);
count += 1;
}
# Surface area is estimated by adding the areas of triangles created
# by the current cell, the cell below it, the cell to the right of it,
# and the cell to the lower right of it.
# Estimate over null cells by keeping the last known value.
if (!IsNull(targetRaster))
current = targetRaster;
if (!IsNull(targetRaster[lin,col+1]))
right = targetRaster[lin,col+1];
if (!IsNull(targetRaster[lin+1,col]))
down = targetRaster[lin+1,col];
if (!IsNull(targetRaster[lin+1,col+1]))
downright = targetRaster[lin+1,col+1];
surface += .5*sqrt(sqr(yscale*current*zscale-yscale*right*zscale)
+sqr(xscale*current*zscale-xscale*down*zscale)
+sqr(xscale*yscale));
surface += .5*sqrt(sqr(yscale*downright*zscale-yscale*right*zscale)
+sqr(xscale*downright*zscale-xscale*down*zscale)
+sqr(xscale*yscale));
cells += 1;
}
CloseRaster(targetRaster);
# Avoid division by zero when calculating mean and standard deviation.
if (count > 1) {
mean = sum / count;
stdDev = sqrt((sumsqr - sqr(sum) / count) / (count - 1));
area = count * xscale * yscale;
perimeter = MyRgn.GetPerimeter() * GetUnitConvDist(coordUnit$, "m");
centroid = MyRgn.GetCentroid();
}
}
cbRedraw();
}
# Called when a distance unit is selected
proc cbDistUnits()
{
distScale = GetUnitConvDist("meters", distUnits.GetValueStr() );
cbRedraw();
}
# Called when an area unit is selected
proc cbAreaUnits()
{
areaScale = GetUnitConvArea("square meters", areaUnits.GetValueStr() );
cbRedraw();
}
# Called when the Save As button is pressed. Saves statistics to text file.
proc cbSave()
{
local class FILE outfile;
local numeric larea, lperimeter, lsurface;
# Scale the values to specified units.
larea = areaScale * area;
lperimeter = distScale * perimeter;
lsurface = areaScale * surface;
# Open output file, write to it, and close it
outfile = GetOutputTextFile(_context.ScriptDir, "Choose file...", "txt");
fprintf(outfile, "Raster: %s\nCells: %d\nNull Cells: %d\nMinimum: %.2f\nMaximum: %.2f\nMean: %.2f\nStandard Deviation: %.2f\nArea: %.2f\nPerimeter: %.2f\nCentroid: %.2f, %.2f\nSurface Area: %.2f",
rasterName$, count, cells - count, min, max, mean, stdDev, larea, lperimeter, centroid.x, centroid.y, lsurface);
fprintf(outfile, "\nDistance Units: %s\nArea Units: %s\n\n", distUnits.GetValueStr(), areaUnits.GetValueStr() );
fclose(outfile);
}
# Called when the Close button is pressed. Closes the dialog.
proc cbClose()
{
polyTool.Managed = 0;
ScriptGadget.Managed = 1;
dlgwin.Close(0);
if (setDefaultWhenClose)
{
setDefaultWhenClose = false;
View.SetDefaultTool();
}
}
##################################################################################
# The following script functions will be called (if used in the script) when
# the appropriate action or event occurs as described in the comments before each.
# To use a function, uncomment the lines containing the 'func' definition
# and ending brace '}' by removing the leftmost '#' on the line and add the
# function code between the two lines.
# Called the first time the tool is activated.
# If the tool implements a dialog it should be created (but not displayed) here.
proc OnInitialize ()
{
# variables for setting up the control dialog
local string xml$;
local numeric err;
if (Layout) {
WidgetAddCallback(Layout.GroupSelectedCallback, cbGroup);
activeGroup = Layout.ActiveGroup;
}
else
activeGroup = Group;
WidgetAddCallback(activeGroup.LayerSelectedCallback, cbLayer);
# add the polygon tool
polyTool = ViewCreatePolygonGadget(View);
ToolAddCallback(polyTool.ActivateCallback, cbToolApply);
# string with dialog specification
xml$ = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "smlforms.dtd">
<root>
<dialog id="regstats" Title="Area Statistics" Buttons="">
<groupbox ExtraBorder="2" Orientation="horizontal">
<canvas id="canvas" Height="155" Width="200"/>
</groupbox>
<pane Orientation="horizontal">
<label WidthGroup="1">Distance Units </label>
<combobox id="distUnits" WidthGroup="2" OnSelection="cbDistUnits()"/>
</pane>
<pane Orientation="horizontal">
<label WidthGroup="1">Area Units </label>
<combobox id="areaUnits" WidthGroup="2" OnSelection="cbAreaUnits()"/>
</pane>
<pane Orientation="horizontal">
<label HorizontalResize="Expand"> </label>
<pushbutton id="saveAsBtn" Name=" Save As... " HorizResize="Fixed" OnPressed="cbSave()"/>
<pushbutton id="closeBtn" Name=" Close " HorizResize="Fixed" OnPressed="cbClose()"/>
</pane>
</dialog>
</root>';
### parse XML string; returns an error code (number < 0 ) if there are syntax errors
local class XMLDOC doc;
err = doc.Parse(xml$);
if (err < 0)
{
PopupError(err); # Popup an error dialog. "Details" button shows syntax errors.
Exit();
}
### declare class instance for the dialog element in the XML structure
### and get the dialog handle from the XML structure.
### Pop up an error dialog and exit if the dialog ID can't be found in the XML.
class XMLNODE dlgnode;
dlgnode = doc.GetElementByID("regstats");
if (dlgnode == 0)
{
PopupMessage("Could not find dialog node in XML document");
Exit();
}
### set the XML structure in memory as the source for the dialog.
dlgwin.SetXMLNode(dlgnode);
err = dlgwin.CreateModeless();
if (err < 0)
{
PopupError(err); # Popup an error dialog. "Details" button shows syntax errors.
Exit();
}
# get handles for dialog controls
canvas = dlgwin.GetCtrlByID("canvas");
distUnits = dlgwin.GetCtrlByID("distUnits");
areaUnits = dlgwin.GetCtrlByID("areaUnits");
saveAsBtn = dlgwin.GetCtrlByID("saveAsBtn");
distUnits.AddUnitItems("Length");
distUnits.SetSelectedItemIndex(0);
areaUnits.AddUnitItems("Area");
areaUnits.SetSelectedItemIndex(0);
} # end of OnInitialize
# Called when tool is to be destroyed, will not be called if tool was never activated.
# If the tool implements a dialog it should be destroyed here.
# proc OnDestroy () {
# } # end of OnDestroy
# Called when tool is activated.
# If the tool implements a dialog it should be "managed" (displayed) here.
proc OnActivate ()
{
local numeric err;
checkLayer();
polyTool.Managed = 1;
polyTool.HasPosition = 0;
cells = 0; min = 0; max = 0; mean = 0; stdDev = 0; sum = 0; sumsqr = 0; count = 0; surface = 0; area = 0; perimeter = 0;
centroid.x = 0; centroid.y = 0;
distScale = 1;
areaScale = 1;
err = dlgwin.Open();
setDefaultWhenClose = true;
cbRedraw();
} # end of OnActivate
# 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);
polyTool.Managed = 0;
} # end of OnDeactivate
# Called when tool is to be 'suspended' during a redraw operation.
# proc OnSuspend () {
# } # end of OnSuspend
# Called when tool is to be 'resumed' after a redraw operation.
# If the tool displays any graphics they should be updated by this function.
# proc OnResume () {
# } # end of OnResume
# Called when user presses 'left' pointer/mouse button.
# proc OnLeftButtonPress () {
# } # end of OnLeftButtonPress
# Called when user presses 'right' pointer/mouse button.
#proc OnRightButtonPress ()
# {
# } # end of OnRightButtonPress
# Called when user presses 'middle' pointer/mouse button.
# proc OnMiddleButtonPress () {
# } # end of OnMiddleButtonPress
# Called when user releases 'left' pointer/mouse button.
# proc OnLeftButtonRelease () {
# } # end of OnLeftButtonRelease
# Called when user releases 'right' pointer/mouse button.
# proc OnRightButtonRelease () {
# } # end of OnRightButtonRelease
# Called when user releases 'middle' pointer/mouse button.
# proc OnMiddleButtonRelease () {
# } # end of OnMiddleButtonRelease
# Called when user moves cursor if no button being pressed
# proc OnPointerMoveNoButton () {
# } # end of OnPointerMoveNoButton
# Called when user moves cursor while holding down button
# proc OnPointerMoveWithButton () {
# } # end of OnPointerMoveWithButton
# Called when cursor enters window associated with view.
# proc OnEnterWindow () {
# } # end of OnEnterWindow
# Called when cursor leaves window associated with view.
# proc OnLeaveWindow () {
# } # end of OnLeaveWindow
# Called when user presses 'key' on keyboard.
# proc OnKeyPress (key) {
# } # end of OnKeyPress