Syntax Highlighing:
comments, key words, predefined symbols, class members & methods, functions & classes
# RASTPROF.SML
# View ToolScript
# This sample toolscript allows the user to draw a profile line in the View using a 2-point line tool.
# It accumulates the raster values from the active raster along the line and then draws a
# graph showing the profile of the raster along the line. The profile can be constructed using
# raw raster values or values averaged over a 3 x 3, 5 x 5, or 7 x 7 focal area.
#
# The active layer must be a continuous-value raster; composite rasters cannot be used.
# Requires TNT version 2012:78 or later.
# Version 26 August 2013
# Changed the function to create the line tool to ViewCreateLineGadget()
# 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}
# 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 GUI_DLG dlgwin;
class GUI_CANVAS canvas; # drawing canvas in GUI Dialog window
class GC gc; # graphics context for the canvas
class GUI_GADGET_SEGMENT profileLine; # tool for drawing 2-point line in View
class STRING rasterName$;
numeric doGraph, hasNull;
class POINT2D startpoint;
class POINT2D endpoint;
array numeric value[1000000];
array numeric draw[1000000];
array numeric graphx[3], graphy[3]; # arrays for x & y coordinates of graph axes
numeric min, max, count, nullVal;
numeric setDefaultWhenClose = false;
# 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.Type == "") {
rasterName$ = "Group has no layers!";
}
else {
if (activeGroup.ActiveLayer.Type == "Raster") {
rasterLayer = activeGroup.ActiveLayer;
DispGetRasterFromLayer(targetRaster, rasterLayer);
# Check for null values
if (HasNull(targetRaster)) {
hasNull = 1;
nullVal = NullValue(targetRaster);
}
else
hasNull = 0;
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") {
rasterName$ = "Type not supported!";
}
else {
rasterName$ = rasterLayer.Name;
valid = true;
}
}
else
rasterName$ = "Not a raster!";
}
return valid;
}
# Callback for drawing area expose. Draws text and graph.
proc cbRedraw()
{
local class POLYLINE graphLine; # polyline used to draw profile line(s)
local class POINT2D graphPt; # point for the current profile line vertex
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.DrawTextSetHeight(12);
gc.DrawTextSimple(sprintf("Raster: %s", rasterName$), 0, 10);
if (doGraph == 0) return;
local string min$, max$;
local numeric size;
### Draw graph axes
min$ = sprintf("%d", min);
max$ = sprintf("%d", max);
size = strlen(max$);
# set coordinates of upper end of y-axis
graphx[1] = size * 6.5 + 5;
graphy[1] = 20;
# origin of x and y axes
graphx[2] = graphx[1];
graphy[2] = canvas.GetHeight() - 15;
# right end of x-axis
graphx[3] = canvas.GetWidth() - 5;
graphy[3] = graphy[2];
# draw the axes
gc.DrawPolyLine(graphx, graphy, 3);
gc.DrawTextSetFont("stork");
gc.DrawTextSetHeight(10);
gc.DrawTextSimple(max$, 0, graphy[1]+10);
gc.DrawTextSimple(min$, (size - strlen(min$)) * 6.5, graphy[2]);
gc.DrawTextSimple(sprintf("%d, %d", startpoint.x, startpoint.y), graphx[1], canvas.GetHeight() - 3);
local string str$ = sprintf("%d, %d", endpoint.x, endpoint.y);
gc.DrawTextSimple(str$, graphx[3] - strlen(str$) * 6.5, canvas.GetHeight() - 3);
gc.SetColorName("magenta"); # color for drawing profile line
local numeric xscale, yscale, prevnull;
xscale = (graphx[3] - graphx[2] + 1) / (count - 1);
yscale = (graphy[2] - graphy[1]) / (max - min);
### Draw profile
# add first profile point to the initial polyline
if (value[1] < 0 || value[1] >= 0) # if current value is not null
{
if (max - min == 0) # all values are the same; set point at left end of horizontal profile line
{
graphPt.x = graphx[2]; graphPt.y = graphy[1] + (graphy[2]-graphy[1])*.5;
}
else # set point at left end of profile line (on y-axis)
{
graphPt.x = graphx[2]; graphPt.y = graphy[2] - (value[1] - min) * yscale;
}
graphLine.AppendVertex(graphPt); # add point to profile polyline
}
# loop through remaining items in the profile value array to draw profile line, leaving
# gaps across areas of null cells (if any)
local numeric i;
for i = 2 to count
{
if (value[i] < 0 || value[i] >= 0) # current value is not null
{
if (!(value[i-1] < 0 || value[i-1] >= 0)) # previous value was null; start new profile line segment
{
if (max - min == 0) # all values are the same; set point at left edge of new segment
{
graphPt.x = graphx[2] + (i - 1) * xscale;
graphPt.y = graphy[1] + (graphy[2] - graphy[1]) * .5;
}
else # set point at left end of new segment of profile line
{
graphPt.x = graphx[2] + (i - 1) * xscale;
graphPt.y = graphy[2] - (value[i] - min) * yscale;
}
graphLine.AppendVertex(graphPt); # add point to profile polyline
}
else # previous value was not null, continue current profile
{
if (max - min == 0) # all values are the same; set point for horizontal profile
{
graphPt.x = graphx[2] + (i - 1) * xscale;
graphPt.y = graphy[1] + (graphy[2] - graphy[1]) * .5;
}
else # compute current point position on profile line
{
graphPt.x = graphx[2] + (i - 1) * xscale;
graphPt.y = graphy[2] - (value[i] - min) * yscale;
}
graphLine.AppendVertex(graphPt); # add point to profile polyline
}
}
else # current value is null
{
if (value[i-1] < 0 || value[i-1] >= 0) # previous value is not null;
{
gc.DrawPolyLine2(graphLine); # draw polyline for current profile segment to edge of this null area
graphLine.Clear(); # clear polyline to start new profile segement at next non-null value
}
}
}
gc.DrawPolyLine2(graphLine);
canvas.Refresh();
graphLine.Clear();
}
# 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();
}
# Callback for when user clicks the right mouse button on the line segment tool
proc cbToolApply()
{
if (checkLayer() )
{
local numeric avgSize = StrToNum(dlgwin.GetCtrlValueStr("avgSize") );
# get coordinate transformation from screen to layer coordinates
# (raster column and line coordinates)
local class TRANS2D_MAPGEN trans;
trans = View.GetTransLayerToScreen(rasterLayer, 1);
startpoint = profileLine.start;
endpoint = profileLine.end;
# convert start and end point to view coordinates
startpoint = trans.ConvertPoint2DFwd(startpoint);
endpoint = trans.ConvertPoint2DFwd(endpoint);
# Get x and y step values for sampling raster cell values along profile line
local numeric ystep, xstep;
local class POINT2D cursor;
cursor = startpoint;
count = 0;
ystep = endpoint.y - startpoint.y;
xstep = endpoint.x - startpoint.x;
if (xstep == 0 && ystep == 0) # profile start and end at same position
return;
# use the x/y extents of the profile line to compute step distance along line
if (abs(xstep) > abs(ystep)) {
ystep = ystep / abs(xstep);
xstep = xstep / abs(xstep);
if (ystep == 0) {
count = abs(endpoint.x - startpoint.x);
}
else
count = abs((endpoint.y - startpoint.y) / ystep);
}
else {
xstep = xstep / abs(ystep);
ystep = ystep / abs(ystep);
if (xstep == 0) {
count = abs(endpoint.y - startpoint.y);
}
else
count = abs((endpoint.x - startpoint.x) / xstep);
}
count = count + 1;
doGraph = 1;
max = -1000000;
min = 1000000;
# Loop on line to generate point list.
local numeric i;
for i = 1 to count {
if (avgSize == 1) then # no averaging for raster values
value[i] = targetRaster[round(cursor.y), round(cursor.x)];
else # use FocalMean function to get mean value for averaging area
value[i] = FocalMean(targetRaster, avgSize, avgSize, round(cursor.y), round(cursor.x))
# If the raster value was retrieved from a null cell or a coordinate
# outside the raster extents, value[i] will be 'not a number'.
# Therefore testing if it is < 0 or >= 0 determines whether it is
# a valid value
if (value[i] < 0 || value[i] >= 0)
{
if (value[i] > max)
max = value[i];
if (value[i] < min)
min = value[i];
}
cursor.x = cursor.x + xstep;
cursor.y = cursor.y + ystep;
}
if (max == -1000000 && min == 1000000)
{
max = 0;
min = 0;
}
cbRedraw();
}
} # end of cbToolApply
# Called when the close button is pressed. Closes the dialogs.
proc cbClose()
{
profileLine.Managed = 0;
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 ()
{
if (Layout)
{
WidgetAddCallback(Layout.GroupSelectedCallback, cbGroup);
activegroup = Layout.ActiveGroup;
}
else
activeGroup = Group;
WidgetAddCallback(activegroup.LayerSelectedCallback, cbLayer);
# Add the 2-point line tool (segment)
profileLine = ViewCreateLineGadget(View);
ToolAddCallback(profileLine.ApplyCallback, cbToolApply);
# string with dialog specification
xml$ = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "smlforms.dtd">
<root>
<dialog id="rastprof" Title="Raster Profile" Buttons="">
<groupbox ExtraBorder="2" Orientation="horizontal">
<canvas HorizResize="Expand" id="canvas" Height="175" Width="300"/>
</groupbox>
<pane Orientation = "horizontal">
<label>Height/width of averaging area (cells) </label>
<combobox id = "avgSize" Default = "3" OnSelection = "cbToolApply();">
<item Name = "1" Value = "1"/>
<item Name = "3" Value = "3"/>
<item Name = "5" Value = "5"/>
<item Name = "7" Value = "7"/>
</combobox>
</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("rastprof");
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 handle for drawing canvas in dialog window
canvas = dlgwin.GetCtrlByID("canvas");
} # 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();
profileLine.Managed = 1;
profileLine.HasPosition = 0;
err = dlgwin.Open();
cbRedraw();
setDefaultWhenClose = true;
} # 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 ()
{
setDefaultWhenClose = false;
cbClose();
} # 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