ReconStrikeDipTool.sml

  Download

More scripts: Display Toolbar

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
############################################################
#
# ReconStrikeDipTool.sml		(Tool Script)
#
############################################################
# Started: April 2008
# Author:  Randy Smith, MicroImages, Inc.
# Version April 2013
# Requires TNTmips version 2010 or later
# 		Updated to use new SML tool classes and associated functions
#		added in TNTmips 2010.
#	   Added code to the OnModeChange, OnAccept, and OnCancel functions to properly
#		manage the "ScriptGadget" and associated tool. Fixes a problem with
#		selecting an existing point in Edit and Delete modes.
# Version 1 May 2008
# Requires TNTmips version 2007:73 or later
##############################################################
#
#	PURPOSE:
#
#	This script provides a tool to enable geologists to record estimates
#	of Strike and Dip of bedding determined from a georeferenced aerial or
#	satellite image. Station locations are recorded as points in a
#	Strike/Dip vector object.  A CartoScript included within this Tool Script
#	is used to display appropriately-oriented estimated strike/dip symbols.
# 	The display parameters are automatically saved for the output vector so
#	that the CartoScript styling is retained when the Strike/Dip vector is
#	used later.  The script creates and displays a control dialog for
#	display and selecting measurement criteria.
#	A two-point line tool is provided for drawing the strike direction at any
#	desired locality in the image.  The station location is recorded at the
#	center point of the strike line.  The dip angle is recorded as a category
#	selected by pressing a radiobutton on the scripts control dialog.
#	The dip categories are:
#		horizontal: 	0 degrees
#		gentle: 			1 to 30 degrees
#		moderate:		30 to 60 degrees
#		steep:			over 60 degrees
#		vertical:		90 degrees
#	A strike/dip measurement can be marked as overturned by pressing the
#	Overturned toggle button.
#	ASSUMPTIONS:
#	Dip direction is determined by the right-hand rule, so the strike line
#	should be drawn so that the dip direction is to the right when facing in
#	the strike direction. A push-button on the control dialog reverses the
#	start and end points of the line and thus the strike direction when needed.
# 	For overturned beds, the strike line should also be drawn so that the dip
#	direction is to the right, but pressing the Overturned toggle reverses
#	the strike direction so that the facing direction (top of beds) is to the
#	right rather than the overturned dip direction.
#	The tool script dialog provides separate modes to VIEW, ADD, EDIT, and
#	DELETE points in the Strike/Dip vector object.
#	ADD - the two-point line tool is active for drawing a new strike line.
#	Draw (and reposition if needed) the strike line, then right-click to
#	set the line position.  Choose a dip category and set Overturned if
#	needed, then press ACCEPT to record the point and attached measurements
#	or CANCEL.
#	VIEW - left-click to select the nearest station point and show its
#	recorded values in the control dialog.  Selecting a new station
#	deselects the previous one.  Press CANCEL to deselect the last selected
#	station and reactivate the Mode radiobuttons.
#	EDIT - left-click to select the nearest station point and show its recorded
#	values in the control dialog.  Press ACCEPT to edit the point (or CANCEL to
#	deselect it).  When editing, the two-point line tool is restored at its
#	position for the station; further actions are the same as for ADD.
# 	DELETE - left-click to select the nearest station point and show its recorded
#	values in the control dialog.  Press DELETE to delete the point, or CANCEL.
#	Estimated strike / dip symbols created by the CartoScript follow
#	guidelines set by the Geologic Data Subcommittee, U.S. Federal Geographic
#	Data Committee.
	#
	# 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
##################################################################################
# ToolScript Global Variables
##################################################################################
class GUI_DLG dlg;											# the control dialog
class GUI_CTRL_LABEL promptString;						# handles for dialog controls
class GUI_CTRL_TOGGLEBUTTON togOVERTURNED;
class GUI_CTRL_EDIT_NUMBER strikeFld, dipDirFld;
class GUI_CTRL_TOGGLEBUTTON togGENTLE, togMODERATE, togSTEEP, togHORIZONTAL, togVERTICAL;
class GUI_CTRL_PUSHBUTTON btnACCEPT, btnCANCEL, btnDELETE, btnCLOSE;
class GUI_CTRL_PUSHBUTTON btnREVERSE;
class GUI_FORM_RADIOGROUP idMODE;
class GUI_GADGET_SEGMENT tool;		# two-point line tool
class RVC_RASTER RAST;					# the raster that is the first layer in the group
class RVC_VECTOR StrikeDipVector; 	# vector object containing station location points
class RVC_OBJITEM SDvectObjItem;		# ObjItem for the strike/dip vector object
class GEOREF SDgeoref;		# georeference for the strike/dip vector object
class RVC_GEOREFERENCE rvcGeorefRAST;		# georeference from the background raster
class TRANSMODEL rastCalibModel;				# geometric transformation model from the DEM georeference
class TRANS2D_MAPGEN transRASTobjToMap;		# coordinate transformation from DEM object to its map coordinates
class SR_COORDREFSYS rastCRS, utmCRS;	# coordinate reference system for background raster and an equivalent UTM CRS
class TRANS2D_MAPGEN transGeogUTM;		# coordinate transformation from geographic to UTM coordinates
class STRING coordUnit$;					# id of map coordinate unit used in the raster CRS
class GRE_GROUP activegroup, firstGroup;
class GRE_LAYER activeLayer;
class GRE_LAYER_RASTER rasterLayer;
class GRE_LAYER_VECTOR SDvectorLayer;
class GRE_VECTOR_POINTS SDpoints;
class DATABASE SDdb;
class DBTABLEINFO SDtable;
class DBFIELDINFO SDstationfield;
class STRING dipCategory$;		# category of dip angle: gentle 0-30, moderate 30-60, or steep 60-90
numeric dipDir;               # azimuth of dip direction (0 to 360 degrees clockwise from north)
numeric strikeAng;            # azimuth of strike line
numeric station, overturned;
numeric setDefaultWhenClose;
numeric selectedElementNum;
numeric currentlyEditing = 0;
numeric toolApplied;
numeric waitingForAddConfirmation = 0;
numeric deletedStation;
numeric transGeogCoords;		# flag to indicate map coordinates need to be transformed from
										# geographic to UTM
class POINT3D ctrPt;
string Current_Mode = "ADD";
string genericQuery = "(Outcrop.Station == ";
string currentQuery;
array numeric records[1];
# CartoScript used to draw the strike/dip symbols
class STRING reconSDqry$ = '# reconBedding.qry
# This is a sample script for drawing geologic point
# symbols for strike and dip of bedding, including
# special cases of horizontal, vertical, overturned bedding.
# The dip value is shown as a text label, except in the
# cases of horizontal and vertical bedding.
# Information required for each point includes the strike
# value (degrees), dip value (degrees), and whether the bed is
# overturned.  Each of these values is read from a particular
# field in an attached database table.  To use the script you
# will need to edit the statements that include database references
# to conform to your table name and field names.
# Strike angle must be specified as azimuth (0 to 360 degrees
# clockwise from north).  Dip direction is defined by the
# strike azimuth using the right hand rule: of the two possible
# azimuths for a given strike line, choose the azimuth the
# strike line points toward with the dip direction on the right hand
# side of the strike line. (For overturned beds, substitute facing
# direction [stratigraphic top] for dip direction in the rule).
# The script assumes that the database field for overturned
# bedding is a Logical field, with Yes indicating overturned bed
# and No (default value) indicating upright bedding.
# Modified for Legend samples August 2002.
# Modified to declare all variables October 2005;
# Modified to correct for angle between grid north and true north May 2006.
# Version Nov. 2007
# Requires TNTmips 2007:73 or later
# Modified to adjust scale based on georeference map units.
numeric azStrike, overturned1, red, green, blue;
class STRING dipCat$;
numeric scale, lineWidthMap, lineWidth;
numeric tickLengthMap, tickLength, tickSpaceMap, tickSpace;
numeric horizSizeMap, horizSize, horizTick, horizSpace;
numeric strikeDashMap, strikeDash, halfDash, strikeSpaceMap, strikeSpace;
#numeric halfLength, tickLength, doubTick, halfTick, heightMap, height, offset;
string fontName$, label$;
numeric direction, oppStrike, dipDir, oppDip;
numeric next_x ,next_y, labelLength, shift1, shift2;
class RVC_GEOREFERENCE vGeoref;
class SR_COORDREFSYS vectCRS;
numeric northAng;
class POINT2D point;
string coordUnit$;
###################### Set Parameters ##############################
# Read strike azimuth and dip value from table.field
azStrike = Bedding.StrikeAngle			#### change to fit your data ####
dipCat$ = Bedding.DipCategory;			#### change to fit your data ####
# This variable defines the denominator of the intended map scale.
# It is used as the basis for defining line width and symbol size.
# Example: for 1:24,000 map scale, Scale = 24000
scale = 150000;
# Check if vector has geographic coordinates (units of degrees instead of meters)
# and if so adjust scale factor to draw symbols of appropriate size.
Vect.GetDefaultGeoref(vGeoref);
vectCRS = vGeoref.GetCoordRefSys();
if (vectCRS.IsProjected() <> 1) {
	if (vectCRS.IsLocal() <> 1) {
		scale = scale * 0.00001;
		}
	}
else {	# CRS is projected; check coordinate units to adjust scale
	# get coordinate unit from the first axis of the planar coordinate system
	coordUnit$ = vectCRS.Coordsys.GetAxis(1).Unit.GetSymbol();
	scale = scale * GetUnitConvDist("m", coordUnit$);
	}
# find angle to north at the current point\'s map coordinates
class TRANS2D_MAPGEN transObjToMap;
class TRANSMODEL model;
model = vGeoref.GetCalibModel();
vGeoref.GetTransParm(transObjToMap, 0, model);
point.x = Vect.Point.Internal.x;		# object coordinates of current point
point.y = Vect.Point.Internal.y;
point = transObjToMap.ConvertPoint2DFwd(point);		# convert to map coordinates
northAng = vectCRS.ComputeAngleToNorth(point);
# subtract angle to north from stored strike value for display
azStrike = azStrike - northAng;
# red, green, blue variables define the color of the symbols and label
red = 0;			green = 0;			blue = 0;
# These variables define the dimensions and line widths of the
# symbol. LineWidthMap is the desired line width in mm, assuming
# vector coordinates are in meters.
lineWidthMap = 0.15;
strikeDashMap = 1.375;
strikeSpaceMap = 0.5;
tickLengthMap = 0.875;
tickSpaceMap = 0.5;
horizSizeMap = 2.125;
if (DrawingLegendView == 1) {
#	strikeLength = 0.5 * SampleRect.GetWidth();
	lineWidth = 0.08 * strikeLength;
	}
else {
	lineWidth = lineWidthMap * scale / 1000;
	strikeDash = strikeDashMap * scale / 1000;
	halfDash = strikeDash * 0.5;
	strikeSpace = strikeSpaceMap * scale / 1000;
	tickLength = tickLengthMap * scale / 1000;
	tickSpace = tickSpaceMap * scale / 1000;
	horizSize = horizSizeMap * scale / 1000;
	horizTick = horizSize * .35;
	horizSpace = horizSize * .15;
	}
######################### Process ############################
# Convert strike azimuth to internal coordinate system.
direction = -(azStrike - 90);
if (direction < 0)
	direction = direction + 360;
oppStrike = direction -180;
dipDir = direction - 90;
oppDip = dipDir - 180;
# Set line color, width, and end type
LineStyleSetColor(red, green, blue);		# set symbol color
LineStyleSetLineWidth(lineWidth);
LineStyleSetCapJoinType(1,1); 	# square ends of lines
########### Draw symbol
LineStyleDropAnchor(0);		# drop anchor at point location
# Special symbol for horizontal bedding (cross in circle)
if (dipCat$ == "HORIZONTAL")
	{
	LineStyleMoveTo(0, horizSpace);
	LineStyleLineTo(0, horizTick);
	LineStyleMoveToAnchor(0);
	LineStyleMoveTo(90, horizSpace);
	LineStyleLineTo(90, horizTick);
	LineStyleMoveToAnchor(0);
	LineStyleMoveTo(180, horizSpace);
	LineStyleLineTo(180, horizTick);
	LineStyleMoveToAnchor(0);
	LineStyleMoveTo(270, horizSpace);
	LineStyleLineTo(270, horizTick);
	}
else
	{
	# draw dashed strike line with center at point
	LineStyleMoveTo(oppStrike, (1.5 * strikeDash) + strikeSpace);
	LineStyleLineTo(direction, strikeDash);
	LineStyleMoveTo(direction, strikeSpace);
	LineStyleLineTo(direction, strikeDash);
	LineStyleMoveTo(direction, strikeSpace);
	LineStyleLineTo(direction, strikeDash);
	LineStyleMoveToAnchor(0);
	if (Bedding.Overturned)
		{
		LineStyleMoveTo(oppStrike, halfDash);
		LineStyleDrawArc(0, 0, halfDash, halfDash, direction, -180, 0);
		LineStyleMoveToAnchor(0);
		dipDir = oppDip;
		}
	if (dipCat$ == "GENTLE")	# draw symbol with single tick mark
		{
		LineStyleLineTo(dipDir, tickLength);
		}
	else if (dipCat$ == "MODERATE")	# draw symbol with two tick marks, centered, tick length apart
		{
		LineStyleMoveTo(direction, tickSpace * 0.5);
		LineStyleLineTo(dipDir, tickLength);
		LineStyleMoveToAnchor(0);
		LineStyleMoveTo(oppStrike, tickSpace * 0.5);
		LineStyleLineTo(dipDir, tickLength);
		}
	else if (dipCat$ == "STEEP")	# draw symbol with three tick marks
		{
		LineStyleLineTo(dipDir, tickLength);	# draw center tick mark
		LineStyleMoveToAnchor(0);
		LineStyleMoveTo(direction, tickSpace);	# draw second tick mark
		LineStyleLineTo(dipDir, tickLength);
		LineStyleMoveToAnchor(0);
		LineStyleMoveTo(oppStrike, tickSpace);
		LineStyleLineTo(dipDir, tickLength);
		}
	else if (dipCat$ == "VERTICAL")
		{
		LineStyleLineTo(dipDir, tickLength);
		LineStyleMoveToAnchor(0);
		LineStyleLineTo(oppDip, tickLength);
		}
	}';
# Basic ToolScript procedures
##################################################################################
proc OnRightButtonPress();
proc OnLeftButtonPress();
proc OnInitialize ();
proc OnDestroy ();
proc OnActivate ();
proc OnDeactivate ();
# Procedures called from interaction with the dialog
##################################################################################
proc OnModeChange();
proc OnGentlePressed();
proc OnModeratePressed();
proc OnSteepPressed();
proc OnHorizontalPressed();
proc OnVerticalPressed();
proc OnOverturnedChanged();
proc OnReverseStrikeLine(class LINETOOL tool);
proc OnAccept();
proc OnCancel();
proc OnDelete();
proc OnClose();
# Functions used to manage the Strike and Dip data
##################################################################################
func addPointToVector(class POINT3D pt);
proc addRecordToDatabase( numeric pointElemNumAdded );
proc UpdateQuery();
proc deleteStation();
# Functions used to initialize or maintain the display layers and databases
##################################################################################
func checkLayer();
proc checkGeoref();
proc createDestVectors();
func createVectorGeoreference();
func vectorLayerExists(string name);
proc addVectorsToDisplay();
proc initializeBeddingDatabaseTable();
# Functions used to manage callbacks
##################################################################################
proc OnLineSet();
proc cbToolApply();
proc cbLayer();
proc cbGroup();
proc cbClose();
##################################################################################
#### Callback for when the active layer changes
proc cbLayer( ) {
	if (!checkLayer() ) {
		string message$ = "The First Layer: "+activeLayer.Name+" is not a raster.";
		PopupMessage(message$);
		}
	else
		checkGeoref();
}
##################################################################################
#### Callback for when the active group changes
proc cbGroup( ) {
	activegroup = Layout.ActiveGroup;
	WidgetAddCallback(activegroup.LayerSelectedCallback, cbLayer);
	cbLayer();
}
###########################################################
### Procedure to check georeference of background raster; if it uses a geographic coordinate
### system, set up a coordinate transformation to the appropriate UTM zone
### to transform map coordinates used to determine strike values.
### Called by onInitialize() and by cbLayer()
proc checkGeoref () {
	local class RECT3D extentsObj;
	local class POINT2D center;
	transGeogCoords = 0;
	rvcGeorefRAST.OpenLastUsed(RAST);
	rastCRS = rvcGeorefRAST.GetCoordRefSys();
	rastCalibModel = rvcGeorefRAST.GetCalibModel();
	rvcGeorefRAST.GetTransParm(transRASTobjToMap, 0, rastCalibModel);
	if (rastCRS.IsProjected() <> 1) {		# raster CRS is not projected
		if (rastCRS.IsLocal() <> 1) {		# raster CRS also is not local, then must be geographic (lat/lon)
			transGeogCoords = 1;
			RAST.GetExtents(extentsObj);
			center = extentsObj.center;
			center = transRASTobjToMap.ConvertPoint2DFwd(center);  # raster center point in map coordinates, used to set UTM zone
			# variables for setting up UTM CRS for transforming raster map coordinates in tool
			local class SR_COORDSYS utmCoordSys;		# coordinate system
			local class SR_COORDOPDEF projectDef;		# projection parameters and method
			local class SR_COORDOPMETHOD method;
			local class SR_DATUM datum;
			datum = rastCRS.Datum;							# get datum from the raster
			utmCoordSys.Assign("Projected2D_EN_m");	# assign projected coordinate system
			method.Assign("1909");							# assign using numeric ID for Transverse Mercator projection
			projectDef.Method = method;					# assign method to projection
			# assign projection parameters that are the same for all UTM zones
			projectDef.SetParmValueNum("LatitudeOfNaturalOrigin", 0);
			projectDef.SetParmValueNum("ScaleFactorAtNaturalOrigin", 0.9996);
			projectDef.SetParmValueNum("FalseEasting", 500000);
			# assign false northing based on whether north or south of equator
			if ( center.y < 0) then
				projectDef.SetParmValueNum("FalseNorthing", 10000);
			else
				projectDef.SetParmValueNum("FalseNorthing", 0);
			# assign longitude of natural origin (central meridian of UTM zone) based on longitude
			local numeric zoneNum, centMerid;		# UTM zone and longitude of central meridian
			zoneNum = ceil( (180 + center.x) / 6 );
			centMerid = (zoneNum * 6) - 183;
			projectDef.SetParmValueNum("LongitudeOfNaturalOrigin", centMerid);
			# create defined UTM coordinate reference system
			utmCRS.Create(utmCoordSys, datum, projectDef);
			# setup coordinate transformation from raster's geographic CRS to the defined UTM CRS.
			transGeogUTM.InputCoordRefSys = rastCRS;
			transGeogUTM.OutputCoordRefSys = utmCRS;
			}
		}
	}
##################################################################################
#### Checks the first layer to see if it is valid raster
func checkLayer( )
	{
	local numeric valid = false;
	local class GRE_LAYER layer = firstGroup.FirstLayer;
	if (layer.TypeID == "Surface") then
		layer = layer.NextLayer;
	# If layer is a raster, get the object from the layer and check its datatype.
	if (layer.TypeID != "")
		{
		if (layer.TypeID == "Raster")
			{
			DispGetRasterFromLayer(RAST, layer);
			rasterLayer = layer;
			valid = true;
			}
		}
	return valid;
	}
##################################################################################
#### Creates the vector objects to store the resultant points and outcrop trace lines
proc createDestVectors( )
	{
	local string flags$ = "Polygonal,3DVector";
	GetOutputVector(StrikeDipVector, flags$, "", Group.Extents);
	createVectorGeoreference();
	}
##################################################################################
#### Creates the vector georeference from the raster if necessary
func createVectorGeoreference( )
	{
	# get the last used georeference object (returns 0 if none)
	SDgeoref	= GetLastUsedGeorefObject(StrikeDipVector);
	if (!SDgeoref)
		{
		CreateImpliedGeoref(StrikeDipVector, rastCRS);
		SDgeoref = GetLastUsedGeorefObject(StrikeDipVector);
		}
	}
##################################################################################
#### This function is used to check if the vector is already in a layer in the View
func vectorLayerExists( string name ) {
	local class VECTOR V;
	# Set the active layer to the top layer in the first group
	firstGroup.SetActiveLayer(firstGroup.LastLayer);
	# Cycle through all the layers in the group
	while (firstGroup.ActiveLayer != 0)
		{
		if (firstGroup.ActiveLayer.TypeID == "Vector")
			{
			DispGetVectorFromLayer(V, firstGroup.ActiveLayer);
			# If the vector layer has the same name, return true
			if (V.$Info.Name == name)
				{
				CloseVector(V);
				return true;
				}
			CloseVector(V);
			}
		# If there are no more layers to check, return false
		if (firstGroup.ActiveLayer.PrevLayer == 0) then
			return false;
		# Set the active layer to the next one down
		firstGroup.SetActiveLayer(firstGroup.ActiveLayer.PrevLayer);
		}
	return false;
	}
##################################################################################
#### Add the vector to the display group if it is not already there
proc addVectorsToDisplay( ) {
	# Check if the StrikeDipVector layer does not already exist
	if(!vectorLayerExists(StrikeDipVector.$Info.Name))
		{
		# Add the vector to the View
		SDvectorLayer = GroupQuickAddVectorVar(firstGroup, StrikeDipVector);
		SDvectorLayer.IgnoreExtents = 1;
		}
	else
		{
		SDvectorLayer = firstGroup.ActiveLayer;
		CloseVector(StrikeDipVector);
		DispGetVectorFromLayer(StrikeDipVector, SDvectorLayer);
		SDvectorLayer.IgnoreExtents = 1;
		}
	# Set up how the layers display points, lines and polygons
	SDpoints = SDvectorLayer.Point;
	# Use the modified bedding.qry CartoScript developed by MicroImages to
	# be used by this ToolScript if it exists in the same directory
	SDpoints.Select.Mode = "All";
	SDvectorLayer.Point.StyleMode = "ByScript";
	SDvectorLayer.Point.Script = reconSDqry$;
	SDvectorLayer.SaveDispParmSubObject();
	}
###########################################################
#### Convert line tool vertices from screen to map coordinates
func class POINT3D setPtMap(class POINT3D pt3d)
	{
	local class POINT2D tmp;
	tmp.x = pt3d.x;
	tmp.y = pt3d.y;
	# convert from screen to view coords
	tmp = TransPoint2D(tmp, ViewGetTransViewToScreen(View, 1));
	# convert from view to map coords
	tmp = TransPoint2D(tmp, ViewGetTransMapToView(View, rasterLayer.MapRegion.CoordRefSys, 1));
	# return a 3D point with map coordinates
	local class POINT3D retPnt;
	retPnt.x = tmp.x;
	retPnt.y = tmp.y;
	return retPnt;
	}
##################################################################################
#### Compute the center point of the line tool
func class POINT3D computeCenterPoint(class POINT3D pt1, class POINT3D pt2)
	{
	local class POINT3D ctr;
	ctr.x = (pt1.x + pt2.x) / 2;
	ctr.y = (pt1.y + pt2.y) / 2;
	return ctr;
	}
##################################################################################
#### Convert geographic coordinates to planar coordinates in the designated UTM CRS
func class POINT2D convertGeogToUTM(class POINT2D geogPt)
	{
	local class POINT2D mapCoords;	# 2D map coordinates
	local class POINT2D utmPt;			# 2D point to return
	mapCoords.x = geogPt.x;		# read geographic coordinates from 3D point to 2D point
	mapCoords.y = geogPt.y;
	mapCoords = TransPoint2D(mapCoords, transGeogUTM);		# convert 2D point to UTM
	utmPt.x = mapCoords.x;	# assign coordinate values to 3D point to return
	utmPt.y = mapCoords.y;
	return utmPt;
	}
##################################################################################
#### Convert planar coordinates in the designated UTM CRS back to geographic
func class POINT3D convertUTMtoGeog(class POINT3D utmPt) {
	local class POINT2D mapCoords;	# 2D map coordinates
	local class POINT3D geogPt;			# 3D point to return
	mapCoords.x = utmPt.x;
	mapCoords.y = utmPt.y;
	mapCoords = TransPoint2D(mapCoords, transGeogUTM, 1);	# inverse transformation
	geogPt.x = mapCoords.x;
	geogPt.y = mapCoords.y;
	return geogPt;
	}
##################################################################################
#### Find angle between grid north and true north for station location (center point)
func findAngleToNorth (class POINT3D ctr)
	{
	local class POINT2D mapCoords;
	local class SR_COORDREFSYS ptCRS;
	if (transGeogCoords == 1) {
		ctr = convertGeogToUTM(ctrPt);
		ptCRS = utmCRS;
		}
	else {
		ptCRS = rastCRS;
		}
	return ptCRS.ComputeAngleToNorth(ctr);
	}
##################################################################################
#### Add the point to the StrikeDipVector and return its element number
func addPointToVector(class POINT3D pt)
	{
	local numeric x = pt.x;
	local numeric y = pt.y;
	VectorAddPoint(StrikeDipVector, x, y);
	VectorValidate(StrikeDipVector);
	return FindClosestPoint(StrikeDipVector, x, y, SDgeoref);
}
##################################################################################
#### Add a record to the StrikeDipVector point database with appropriate values
proc addRecordToDatabase( numeric pointElemNumAdded )
	{
	local class POINT3D lStart, lEnd;
	# Get the current end points of the line tool to save in the record
	lStart = setPtMap(tool.start);
	lEnd = setPtMap(tool.end);
	# If editing, use the same station number
	if (Current_Mode == "EDIT")
		{
		station = deletedStation;
		}
	# If adding a new one, set as the highest station incremented by 1
	else
		{
		station = 0;
		local numeric tempVal;
		local numeric numRecords = NumRecords(SDtable);
		local numeric i;
		for i = 1 to numRecords
			{
			tempVal = TableReadFieldNum(SDtable, "Station", i);
			if (tempVal > station)
				{
				station = tempVal;
				}
			}
		station = station + 1;
		}
	# Write the record for the Strike Dip point and attach it
	records[1] = TableWriteRecord(SDtable, 0, station, strikeAng, dipCategory$, dipDir,
												overturned, lStart.x, lStart.y, lEnd.x, lEnd.y);
	TableWriteAttachment(SDtable, pointElemNumAdded, records, 1, "point");
	# close the database and remove the strike/dip vector
	CloseDatabase(SDdb);
	GroupRemoveLayer(firstGroup, SDvectorLayer);
	addVectorsToDisplay();
	initializeBeddingDatabaseTable();
	}
##################################################################################
#### This function deletes the point and database record that
#### are related to the station that is being deleted.
proc deleteStation( )
	{
	local numeric numToDelete = 0;
	local numeric i;
	local array numeric tempRecords[10];
	local numeric tempStation;
	local numeric numRecords;
	# Turn off the highlighted point
	SDpoints.HighlightSingle(selectedElementNum, "Toggle");
	# Get the station number from the selected point
	deletedStation = StrikeDipVector.point[selectedElementNum].Bedding.Station;
	# Scan through and delete the Strike and Dip station
	numPoints = NumVectorPoints(StrikeDipVector);
	local array numeric pointsToDelete[numPoints];
	local numeric pointToDelete;
	for i = 1 to numPoints
		{
		tempStation = StrikeDipVector.point[i].Bedding.Station;
		if (tempStation == deletedStation)
			{
			# Unnattach the records if the point is being marked to delete
			numRecords = TableReadAttachment(SDtable, i, tempRecords, "point");
			TableRemoveAttachment(SDtable, i, tempRecords, numRecords, "point");
			# Save point number to delete
			numToDelete = numToDelete + 1;
			pointsToDelete[numToDelete] = i;
			}
		}
	# Delete all the marked points
	if (numToDelete > 0)
		{
		VectorDeletePoints(StrikeDipVector, pointsToDelete, numToDelete);
		}
	# Remove all the records that are no longer attached
	TableRemoveUnattachedRecords(SDtable);
	VectorValidate(StrikeDipVector);
	}
##################################################################################
#### Create the vector point database tables for the StrikeDipVector
proc initializeBeddingDatabaseTable( )
	{
	local string name$ = "Bedding";
	local string desc$ = "Strike and dip bedding";
	SDdb = OpenVectorPointDatabase(StrikeDipVector);
	# If the table doesn't exist it needs to be created
	if( !TableExists(SDdb, name$) )
		{
		SDtable = TableCreate(SDdb, name$, desc$);
		SDtable.OneRecordPerElement = 1;
		# Create the fields
		TableAddFieldInteger(SDtable, "Station", 8);
		TableAddFieldInteger(SDtable, "StrikeAngle", 11);
		TableAddFieldString(SDtable, "DipCategory", 11);
		TableAddFieldInteger(SDtable, "DipDirection", 12);
		TableAddFieldInteger(SDtable, "Overturned", 10);
		TableAddFieldFloat(SDtable, "ToolPos1_x", 12, 4);
		TableAddFieldFloat(SDtable, "ToolPos1_y", 12, 4);
		TableAddFieldFloat(SDtable, "ToolPos2_x", 12, 4);
		TableAddFieldFloat(SDtable, "ToolPos2_y", 12, 4);
		}
	# Else the table does exist, get the info for it
	else
		{
		SDtable = DatabaseGetTableInfo(SDdb, name$);
		}
	SDstationfield = FieldGetInfoByName(SDtable, "Station");
	}
#################################################################################
#### Callback for when line tool position is set
proc OnLineSet()
	{
	promptString.SetLabel("Right-click to set line position.");
	}
##################################################################################
#### Callback for right click with LINETOOL.
proc cbToolApply()
	{
	local class POINT3D lStart, lEnd;
	local numeric distance, azimuth, elevation;
	local numeric northAng;
	promptString.SetLabel("Computing Data...");
	lStart = setPtMap(tool.start);
	lEnd = setPtMap(tool.end);
	# check if tool start and end points are the same for non-horizontal dip
	if ( (lStart.x == lEnd.x && lStart.y == lEnd.y) && !togHORIZONTAL.GetValue() )
		promptString.SetLabel("Start/end points coincide. Draw line.");
	else
		{
		ctrPt = computeCenterPoint(lStart, lEnd);
		if (togHORIZONTAL.GetValue() )	# dip category set to HORIZONTAL
			{
			strikeAng = -1;		# strike and dip direction are undefined, so set to -1
			dipDir = -1;
			strikeFld.SetValueNum(-1);
			dipDirFld.SetValueNum(-1);
			}
		if (!togHORIZONTAL.GetValue() )	# dip category set to GENTLE, MODERATE, STEEP, or VERTICAL
			{
			# find azimuth of line
			Displacement3Dd(lStart.x, lStart.y, lStart.z, lEnd.x, lEnd.y, lEnd.z, distance, azimuth, elevation);
			northAng = findAngleToNorth(ctrPt);
			strikeAng = round(azimuth + northAng);
			dipDir = strikeAng + 90;
			if (dipDir > 360) then
				dipDir = dipDir - 360;
			if (Current_Mode == "ADD" or Current_Mode == "EDIT")
				togOVERTURNED.SetValue(0, 0);	# set toggle to OFF without calling the on change callback
			strikeFld.SetValueNum(strikeAng);
			dipDirFld.SetValueNum(round(dipDir));
			}
		togOVERTURNED.SetEnabled(1);
		# Manage the controls in the display
		idMODE.SetEnabled(0);
		btnCLOSE.SetEnabled(0);
		btnACCEPT.SetEnabled(1);
		btnCANCEL.SetEnabled(1);
		btnREVERSE.SetEnabled(1);
		if (Current_Mode == "ADD")
			{
			promptString.SetLabel("Accept this New point?");
			}
		else if (Current_Mode == "EDIT")
			{
			promptString.SetLabel("Accept this Edited point?");
			}
		}
	} # end cbToolApply
##################################################################################
#### Manages the control dialog when the active mode has been changed
proc OnModeChange( ) {
	# Get the current Mode
	Current_Mode = idMODE.GetValueStr();
	# Set controls to default values
	strikeFld.SetValueNum(0);
	dipDirFld.SetValueNum(0);
	togOVERTURNED.SetEnabled(1);
	togOVERTURNED.SetValue(0,0);
	togOVERTURNED.SetEnabled(0);
	btnACCEPT.SetEnabled(0);
	btnCANCEL.SetEnabled(0);
	btnDELETE.SetEnabled(0);
	# Based on Mode, set the controls and tool to appropriate settings
	if ( Current_Mode == "VIEW") {
		promptString.SetLabel("Select Point to Toggle ON");
		tool.managed = 0;
		ScriptGadget.Managed = 1;
	} else if ( Current_Mode == "ADD" ) {
		promptString.SetLabel("Draw Line in Strike Direction");
		tool.managed = 1;
	} else if ( Current_Mode == "EDIT" ) {
		promptString.SetLabel("Select Point to Edit");
		tool.managed = 0;
		ScriptGadget.Managed = 1;
	} else if ( Current_Mode == "DELETE" ) {
		promptString.SetLabel("Select Point to Delete");
		tool.managed = 0;
		ScriptGadget.Managed = 1;
	}
}
#################################################################################
#### Called when the Gentle dip category toggle button is pressed
proc OnGentlePressed()
	{
	if ( togGENTLE.GetValue() )
		{
		dipCategory$ = "GENTLE";
		togMODERATE.SetValue(0, 0);
		togSTEEP.SetValue(0, 0);
		togHORIZONTAL.SetValue(0, 0);
		togVERTICAL.SetValue(0, 0);
		}
	}
#################################################################################
#### Called when the Moderate dip category toggle button is pressed
proc OnModeratePressed()
	{
	if( togMODERATE.GetValue() )
		{
		dipCategory$ = "MODERATE";
		togGENTLE.SetValue(0, 0);
		togSTEEP.SetValue(0, 0);
		togHORIZONTAL.SetValue(0, 0);
		togVERTICAL.SetValue(0, 0);
		}
	}
#################################################################################
#### Called when the Steep dip category toggle button is pressed
proc OnSteepPressed()
	{
	if ( togSTEEP.GetValue() )
		{
		dipCategory$ = "STEEP";
		togGENTLE.SetValue(0, 0);
		togMODERATE.SetValue(0, 0);
		togHORIZONTAL.SetValue(0, 0);
		togVERTICAL.SetValue(0, 0);
		}
	}
#################################################################################
#### Called when the Horizontal dip category toggle button is pressed
proc OnHorizontalPressed()
	{
	if ( togHORIZONTAL.GetValue() )
		{
		dipCategory$ = "HORIZONTAL";
		togGENTLE.SetValue(0, 0);
		togMODERATE.SetValue(0, 0);
		togSTEEP.SetValue(0, 0);
		togVERTICAL.SetValue(0, 0);
		}
	}
#################################################################################
#### Called when the Vertical dip category toggle button is pressed
proc OnVerticalPressed()
	{
	if ( togVERTICAL.GetValue() )
		{
		dipCategory$ = "VERTICAL";
		togGENTLE.SetValue(0, 0);
		togMODERATE.SetValue(0, 0);
		togSTEEP.SetValue(0, 0);
		togHORIZONTAL.SetValue(0, 0);
		}
	}
#################################################################################
#### callback for Reverse Strike Line button on dialog
proc OnReverseStrikeLine(class LINETOOL tool)
	{
	local class POINT3D origStart, origEnd;
	origStart = tool.start;
	origEnd = tool.end;
	tool.start = origEnd;
	tool.end = origStart;
	cbToolApply();
	}
##################################################################################
#### Called when the user clicks on the "Set as Overturned" togglebutton.  It
#### adjusts the value of the Strike angle by 180 degrees if it is set as overturned
proc OnOverturnedChanged( )
	{
	# If the Strike Angle is not 0
	if (strikeFld.GetValueNum() != 0)
		{
		# If the angle is less then 180 degrees
		if (strikeAng < 180) then
			strikeAng = strikeAng + 180;
		# If the angle is greater than 180 degrees
		else if (strikeAng > 180) then
			strikeAng = strikeAng - 180;
		strikeFld.SetValueNum(strikeAng);
		}
	}
##################################################################################
#### Called when the user clicks on the Accept button in the dialog.  Actions are
#### based on  the current Mode of the ToolScript
proc OnAccept( )
	{
	# If in ADD Mode, add the computed Strike and Dip data to the Destination Vectors
	if (Current_Mode == "ADD")
		{
		promptString.SetLabel("Saving Data...");
		overturned = togOVERTURNED.GetValue();
		if (overturned) then togOVERTURNED.SetValue(0, 0);
		togOVERTURNED.SetEnabled(0);
		btnREVERSE.SetEnabled(0);
		btnACCEPT.SetEnabled(0);
		btnCANCEL.SetEnabled(0);
		addRecordToDatabase( addPointToVector(ctrPt) );
		waitingForAddConfirmation = 0;
		# Reset the line tool
		tool.HasPosition = 0;
		# Update the display
		ViewRedrawLayer(View, SDvectorLayer);
		selectedElementNum = 0;
		idMODE.SetEnabled(1);
		btnCLOSE.SetEnabled(1);
		strikeFld.SetValueNum(0);
		dipDirFld.SetValueNum(0);
		promptString.SetLabel("Draw Line in Strike Direction");
		}
	# If in EDIT Mode, reload the data or save the new edited data
	else if (Current_Mode == "EDIT")
		{
		# Check if the point has already been selected and in modifying mode
		if ( currentlyEditing )
			{
			promptString.SetLabel("Editing Data...");
			overturned = togOVERTURNED.GetValue();
			if (overturned) then togOVERTURNED.SetValue(0, 0);
			togOVERTURNED.SetEnabled(0);
			btnREVERSE.SetEnabled(0);
			btnACCEPT.SetEnabled(0);
			btnCANCEL.SetEnabled(0);
			# Delete the old station information and add the new station information
			deleteStation();
			addRecordToDatabase( addPointToVector(ctrPt) );
			waitingForAddConfirmation = 0;
			# Update the display
			ViewRedrawLayer(View, SDvectorLayer);
			# Switch to the default tool for left-click
			tool.Managed = 0;
			ScriptGadget.Managed = 1;
			currentlyEditing = 0;
			selectedElementNum = 0;
			# clear the strike and dip direction fields
			strikeFld.SetValueNum(0);
			dipDirFld.SetValueNum(0);
			togOVERTURNED.SetValue(0, 0);
			idMODE.SetEnabled(1);
			btnCLOSE.SetEnabled(1);
			promptString.SetLabel("Select point to Edit");
			}
		# Else the point needs to be loaded and allowed to be modified
		else
			{
			promptString.SetLabel("Loading Data...");
			btnACCEPT.SetEnabled(0);
			btnCANCEL.SetEnabled(0);
			togOVERTURNED.SetEnabled(1);
			btnREVERSE.SetEnabled(1);
			local class POINT2D p1, p2, p3;
			p1.x = StrikeDipVector.point[selectedElementNum].Bedding.ToolPos1_x;
			p1.y = StrikeDipVector.point[selectedElementNum].Bedding.ToolPos1_y;
			p2.x = StrikeDipVector.point[selectedElementNum].Bedding.ToolPos2_x;
			p2.y = StrikeDipVector.point[selectedElementNum].Bedding.ToolPos2_y;
			# Map them to the screen coordinates
			local class TRANS2D_MAPGEN trans;	# transform from map to view
			trans = View.GetTransMapToView(rasterLayer.MapRegion.CoordRefSys, 0);
			p1 = trans.ConvertPoint2DFwd(p1);
			p2 = trans.ConvertPoint2DFwd(p2);
			trans = View.GetTransViewToScreen(0);
			p1 = trans.ConvertPoint2DFwd(p1);
			p2 = trans.ConvertPoint2DFwd(p2);
			# Reset the line tool with the 2 points
			tool.start = p1;
			tool.end = p2;
			# Set the line tool for editing
			currentlyEditing = 1;
			tool.Managed = 1;
			promptString.SetLabel("Modify the line");
			}
		}
	}	# end of OnAccept();
##################################################################################
#### Called when the user clicks on the Cancel button in the dialog.  Actions are
#### based on  the current Mode of the ToolScript
proc OnCancel( )
	{
	strikeFld.SetValueNum(0);
	dipDirFld.SetValueNum(0);
	togOVERTURNED.SetValue(0, 0);
	# If in VIEW mode, unselect the point and reset the control dialog
	if (Current_Mode == "VIEW")
		{
		btnACCEPT.SetEnabled(0);
		btnCANCEL.SetEnabled(0);
		SDpoints.HighlightSingle(selectedElementNum, "Toggle");
		idMODE.SetEnabled(1);
		btnCLOSE.SetEnabled(1);
		promptString.SetLabel("Select point to Toggle ON");
		}
	# If in ADD mode, reset the dialog
	else if (Current_Mode == "ADD")
		{
		togOVERTURNED.SetEnabled(0);
		btnREVERSE.SetEnabled(0);
		btnACCEPT.SetEnabled(0);
		btnCANCEL.SetEnabled(0);
		promptString.SetLabel("Clearing Data");
		# If canceling a computed point, refresh the display
		if (waitingForAddConfirmation) {
			waitingForAddConfirmation = 0;
			ViewRedrawLayer(View, SDvectorLayer);
			}
		idMODE.SetEnabled(1);
		btnCLOSE.SetEnabled(1);
		promptString.SetLabel("Draw Line in Strike Direction");
		}
	# If in EDIT mode, cancel out of selecting a point, or remove the temporary data
	else if (Current_Mode == "EDIT")
		{
		if (togOVERTURNED.GetValue() ) then togOVERTURNED.SetValueNum(0);
		btnREVERSE.SetEnabled(0);
		btnACCEPT.SetEnabled(0);
		btnCANCEL.SetEnabled(0);
		SDpoints.HighlightSingle(selectedElementNum, "Toggle");
		tool.Managed = 0;
		currentlyEditing = 0;
		waitingForAddConfirmation = 0;
		idMODE.SetEnabled(1);
		btnCLOSE.SetEnabled(1);
		promptString.SetLabel("Select point to Edit");
		}
	# If in DELETE mode, deselect the point
	else if (Current_Mode == "DELETE")
		{
		if (togOVERTURNED.GetValue() ) then togOVERTURNED.SetValueNum(0);
		btnDELETE.SetEnabled(0);
		btnCANCEL.SetEnabled(0);
		SDpoints.HighlightSingle(selectedElementNum, "Toggle");
		idMODE.SetEnabled(1);
		btnCLOSE.SetEnabled(1);
		promptString.SetLabel("Select point to Delete");
		}
	# Reset the selected element to nothing
	selectedElementNum = 0;
	} # end OnCancel
##################################################################################
#### Called when the user clicks on the Cancel button in the dialog.  Deletes the
#### selected point and all the records attached to that station.
proc OnDelete( )
	{
	promptString.SetLabel("Deleting Data...");
	idMODE.SetEnabled(0);
	btnCLOSE.SetEnabled(0);
	btnCANCEL.SetEnabled(0);
	btnDELETE.SetEnabled(0);
	# Delete the station and attached records and update the display
	deleteStation();
	ViewRedrawLayer(View, SDvectorLayer);
	strikeFld.SetValueNum(0);
	dipDirFld.SetValueNum(0);
	togOVERTURNED.SetValue(0, 0);
	idMODE.SetEnabled(1);
	btnCLOSE.SetEnabled(1);
	promptString.SetLabel("Select point to Delete");
	selectedElementNum = 0;
	}
##################################################################################
#### Called when the user clicks on the Close button to close the dialog
proc OnClose( )
	{
	tool.Managed = 0;
	setDefaultWhenClose = true;
	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 ()
	{
	local class XMLDOC dlgdoc; # class instance for XML document containing the dialog specification
	local class XMLNODE node;	# node in the XML document corresponding to the dialog
	# Is this a layout?  Set callbacks in case things change
	if (Layout)
		{
		WidgetAddCallback(Layout.GroupSelectedCallback, cbGroup);
		firstGroup = Layout.FirstGroup;
		}
	else
		{
		firstGroup = Group;
		}
	WidgetAddCallback(firstGroup.LayerSelectedCallback, cbLayer);
	# Is the bottom layer a valid DEM raster?
	if ( checkLayer() )
		{
		# Initialize tool
		tool = ViewCreateLineGadget(View);
		ToolAddCallback(tool.ActivateCallback, cbToolApply);
		ToolAddCallback(tool.PositionClearedCallback, OnCancel);
		ToolAddCallback(tool.PositionSetCallback, OnLineSet);
		checkGeoref();
		# Create the destination vectors and their tables
		createDestVectors();
		addVectorsToDisplay();
		initializeBeddingDatabaseTable();
		View.Redraw();
		}
		else
		{
		string message$ = "Active Layer: "+activeLayer.Name+" is not a valid DEM raster.";
		PopupMessage(message$);
		}
	# XML script that specifies the context of the dialog
	local class STRING xml$='<?xml version="1.0"?>
	<root>
		<dialog id="id_StrikeDipDialog" Title="Recon StrikeDip Control Panel" buttons="">
			<groupbox Name=" Current Mode: " ExtraBorder="5">
				<radiogroup id="id_MODE" orientation="Horizontal" Default="ADD" OnSelection="OnModeChange();">
					<item Value="VIEW" Name="View"/>
					<item Value="ADD" Name="Add"/>
					<item Value="EDIT" Name="Edit"/>
					<item Value="DELETE" Name="Delete"/>
				</radiogroup>
			</groupbox>
			<pane Orientation="Horizontal" HorizResize="Fixed">
				<label WidthGroup="1">Strike Azimuth   </label>
				<editnumber Width="6" id="id_STRIKEANGLE" Precision="0" ReadOnly="true" HorizResize="Fixed"/>
			</pane>
			<pane Orientation="Horizontal" HorizResize="Fixed">
				<label WidthGroup="1">Dip Azimuth   </label>
				<editnumber Width="6" id="id_DIPDIRECTION" Precision="0" ReadOnly="true" HorizResize="Fixed"/>
			</pane>
			<groupbox Name=" Dip Category " ExtraBorder="3">
				<pane Orientation="Horizontal">
					<togglebutton id="id_GENTLE" name="Gentle" OnChanged="OnGentlePressed();"/>
					<togglebutton id="id_MODERATE" name="Moderate" OnChanged="OnModeratePressed();"/>
					<togglebutton id="id_STEEP" name="Steep" OnChanged="OnSteepPressed();"/>
				</pane>
				<pane Orientation="Horizontal">
					<togglebutton id="id_HORIZONTAL" name="Horizontal" OnChanged="OnHorizontalPressed();"/>
					<togglebutton id="id_VERTICAL" name="Vertical" OnChanged="OnVerticalPressed();"/>
				</pane>
			</groupbox>
			<togglebutton id="id_OVERTURNED" name="Set as Overturned" OnChanged="OnOverturnedChanged();" Selected="false" Enabled="false"/>
			<pushbutton id = "id_REVERSE" name="Reverse Strike Line" Enabled="false" OnPressed="OnReverseStrikeLine(tool);"/>
			<groupbox Name=" Apply Action: " Orientation="Horizontal" ExtraBorder="5">
				<pushbutton id="id_ACCEPT" Name="Accept" OnPressed="OnAccept();"/>
				<pushbutton id="id_CANCEL" Name="Cancel" OnPressed="OnCancel();"/>
				<pushbutton id="id_DELETE" Name="Delete" OnPressed="OnDelete();"/>
				<pushbutton id="id_CLOSE" Name="Close" OnPressed="OnClose();"/>
			</groupbox>
			<label id="id_PROMPT">Select Prompt</label>
		</dialog>
	</root>';
	# Parse the document to make sure it is valid
	local numeric err = dlgdoc.Parse(xml$);
	if ( err < 0 )
		{
		PopupError( err ); 	# Popup an error dialog. "Details" button shows syntax errors.
		Exit();
		}
	# Find the document
	node = dlgdoc.GetElementByID("id_StrikeDipDialog");
	if ( node == 0 )
		{
		PopupMessage("Could not find dialog node in XML document");
		Exit();
		}
	# Set the XML Node
	err = dlg.SetXMLNode(node);
	if ( err < 0 )
		{
		PopupMessage("Could not set the XML Node");
		Exit();
		}
	# Create a modeless dialog and assign some commonly changed elements to controls
	dlg.CreateModeless();
	idMODE = dlg.GetCtrlByID("id_MODE");
	strikeFld = dlg.GetCtrlByID("id_STRIKEANGLE");
	dipDirFld = dlg.GetCtrlByID("id_DIPDIRECTION");
	togGENTLE = dlg.GetCtrlByID("id_GENTLE");
	togMODERATE = dlg.GetCtrlByID("id_MODERATE");
	togSTEEP = dlg.GetCtrlByID("id_STEEP");
	togHORIZONTAL = dlg.GetCtrlByID("id_HORIZONTAL");
	togVERTICAL = dlg.GetCtrlByID("id_VERTICAL");
	togOVERTURNED = dlg.GetCtrlByID("id_OVERTURNED");
	btnREVERSE = dlg.GetCtrlByID("id_REVERSE");
	btnACCEPT = dlg.GetCtrlByID("id_ACCEPT");
	btnCANCEL = dlg.GetCtrlByID("id_CANCEL");
	btnDELETE = dlg.GetCtrlByID("id_DELETE");
	btnCLOSE = dlg.GetCtrlByID("id_CLOSE");
	promptString = dlg.GetCtrlByID("id_PROMPT");
	togGENTLE.SetValue(1, 0);
	dipCategory$ = "GENTLE";
	OnModeChange();
	} # 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 ()
	{
	tool.Managed = 0;
	CloseDatabase(SDdb);
	CloseVector(StrikeDipVector);
	} # end of OnDestroy
#########################################################################
#### Called when tool is activated.
#### If the tool implements a dialog it should be "managed" (displayed) here.
proc OnActivate () {
	dlg.Open();
	OnModeChange();
	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 ()
	{
	dlg.Close(0);
	setDefaultWhenClose = false;
	}  # end of OnDeactivate
##################################################################################
#### Called when the Left Button is pressed.  This selects the closest point in the
#### StrikeDipVector and displays the information related to that point.  Certain
#### modes use this selected point for their operations
proc OnLeftButtonPress ()
	{
	# Make sure the linetool is not currently active or this will conflict with it
	if (!tool.Managed)
		{
		# Get the point on the screen that was clicked
		local class POINT2D screenPoint, layerPoint;
		screenPoint.x = PointerX;
		screenPoint.y = PointerY;
		# Get the layer coordinates from the screen point and find the closest point
		local class TRANS2D_MAPGEN transparm = View.GetTransLayerToScreen(SDvectorLayer, 1);
		layerPoint = transparm.ConvertPoint2DFwd(screenPoint);
		selectedElementNum = FindClosestPoint(StrikeDipVector, layerPoint.x, layerPoint.y, SDgeoref, 300);
		# If greater then 0, it has found the closest point
		if (selectedElementNum > 0)
			{
			SDpoints.HighlightSingle(selectedElementNum, "Replace");
			# Load the values for that point in the dialog
			strikeFld.SetValueNum(StrikeDipVector.point[selectedElementNum].Bedding.StrikeAngle);
			dipDirFld.SetValueNum(StrikeDipVector.point[selectedElementNum].Bedding.DipDirection);
			dipCategory$ = StrikeDipVector.point[selectedElementNum].Bedding.DipCategory$
			if (dipCategory$ == "HORIZONTAL") then
				togHORIZONTAL.SetValue(1, 1);
			else if (dipCategory$ == "GENTLE") then
				togGENTLE.SetValue(1, 1);
			else if (dipCategory$ == "MODERATE") then
				togMODERATE.SetValue(1, 1);
			else if (dipCategory$ == "STEEP") then
				togSTEEP.SetValue(1, 1);
			else if (dipCategory$ == "VERTICAL") then
				togVERTICAL.SetValue(1, 1);
			togOVERTURNED.SetEnabled(1);
			togOVERTURNED.SetValue(StrikeDipVector.point[selectedElementNum].Bedding.Overturned, 0);
			togOVERTURNED.SetEnabled(0);
			# Update the control dialog settings based on the current mode
			idMODE.SetEnabled(0);
			btnCANCEL.SetEnabled(1);
			btnCLOSE.SetEnabled(0);
			if (Current_Mode == "EDIT")
				{
				promptString.SetLabel("CONFIRM: Edit this point?");
				btnACCEPT.SetEnabled(1);
				}
			else if (Current_Mode == "DELETE")
				{
				promptString.SetLabel("CONFIRM: Delete this point?");
				btnDELETE.SetEnabled(1);
				}
			}
		}
	}  # end of OnLeftButtonPress
##################################################################################
#### Called when the Right Button is pressed.  This is an alternative method of
#### clicking "Accept" in certain situations
proc OnRightButtonPress( )
	{
	if (!tool.Managed)
		{
		if (selectedElementNum > 0)
			{
			if (Current_Mode == "VIEW") then
				OnAccept();
			else if (Current_Mode == "DELETE") then
				OnDelete();
			}
		}
	}	# end of OnRightButtonPress()