regstats2.sml

  Download

More scripts: Display Toolbar

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
# REGSTATS2.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 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.
# Modified from REGSTATS.SML.  Save procedure
# automatically opens the saved text file in the associated software program.
# Requires TNTmips version 2012:78 or later.
# Version 26 August 2013
#		Changed the function to create the polyline tool to ViewCreatePolyLineGadget()
#		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
### 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 class STRING textfile$;
	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
	textfile$ = GetOutputFileName(_context.ScriptDir, "Choose file...", "txt");
	outfile = fopen(textfile$, "w", "ASCII");
	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);
	RunAssociatedApplication(textfile$);
	}
# 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 = ViewCreatePolyLineGadget(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