FlightPlan.sml

  Download

More scripts: Display Toolbar

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
### FlightPlan74.sml
### SML Tool Script
### Requires TNT version 2008:74 or later.
### Tool script for laying out a set of parallel flight lines for aerial imaging
### operations over a target area.  Script provides and manages tools for drawing
### the target area polygon in the View and for drawing a straight line to
### indicate the flight line direction.  The target area can also be loaded from
### a vector, CAD, or region object.  Flight lines are created to fully cover
### the target polygon and are clipped at a buffer polygon surrounding the target
### area at the specified buffer distance.  A custom dialog window provides controls
### for inputing parameters and for activating the drawing actions and other
### procedures.  Flight lines, area boundary, and buffer can be saved as either
### vector or CAD objects, and flight line can be exported to a GPX file.
### Brett Colombe and Randy Smith, MicroImages, March 2007.
### Revised 26 April 2007 to add boundary from vector, CAD, or region and save as CAD.
### Revised 24 June 2007 to change the callback behavior for the Spacing and Buffer distance
### fields in the control dialog to streamline entry of values.
class GRE_GROUP group;
class GRE_VIEW View;
class XmForm form;           	# parent form for dialog window.
class GUI_GADGET_SEGMENT LineTool;	# class for a 2-point line tool
class GUI_GADGET_POLYLINE PolyLineTool;	# class for polyline/polygon tool.
class POINT2D coordstart;
class POINT2D coordend;
class VECTOR vectorLine, vectorPoly, vectorBuffer, vectorPoint, vectorInitLine, temp;
class SR_COORDREFSYS crs; # coordinate reference system class
class GRE_LAYER_VECTOR vectorLineLayer;
class GRE_LAYER_VECTOR vectorPolyLayer;
class GRE_LAYER_VECTOR vectorBufferLayer;
class GRE_LAYER_VECTOR vectorPointLayer;
# class PROMPTNUM spacingdist, bufferdist;
class GUI_DLG dlgwin;		# class instance for the GUI dialog
## handles for dialog controls
class GUI_CTRL_LABEL bufflabel;
class GUI_CTRL_EDIT_NUMBER spacingEN, buffdistanceEN;
class GUI_CTRL_PUSHBUTTON orientationBtn, boundaryBtn, loadboundaryBtn, computeBtn;
class GUI_CTRL_PUSHBUTTON saveBtnV, saveBtnC,  exportBtn, closeBtn;
class GUI_CTRL_EDIT_STRING promptFld;
# Clear all elements in the vector
proc ClearVector (var class VECTOR vectorvar)
	{
	numeric i;
	for i = 1 to NumVectorLines(vectorvar)
		VectorDeleteLine(vectorvar, 1);
	for i = 1 to NumVectorPoints(vectorvar)
		VectorDeletePoint(vectorvar, 1);
	}
# Procedure called when spacing distance is entered.
proc OnSpacing ()
	{
	promptFld.SetValueStr("Enter distance for buffer zone around boundary.");
	bufflabel.SetEnabled(1);
	buffdistanceEN.SetEnabled(1);
	}
# Procedure called when buffer distance is entered.
proc OnBuffDistance ()
	{
	boundaryBtn.SetEnabled(1);
	loadboundaryBtn.SetEnabled(1);
	promptFld.SetValueStr("Create or load a boundary for the flight area.");
	}
# Compute offset distance in degrees
# A given distance in meters will be different degrees based on location
# Thus, recompute distance for each line
proc ComputeDistance (var numeric value, numeric dist, var class POLYLINE baseline)
	{
	numeric distoffset = 0;
	# compute arbitrary offset line if .01 degrees
	class POLYLINE offset;
	baseline.ComputeOffset(.01, offset);
	# determine meters equivalent to .01 degrees offset
	distoffset = ProjDistanceToMeters(crs, baseline.GetVertex(0).x, baseline.GetVertex(0).y, offset.GetVertex(0).x, offset.GetVertex(0).y);
	# determine degrees equivalent to offset meters
	value = .01 * (dist / distoffset);
	}
# Procedure called when Compute Flight Lines button is pressed.
proc ComputeLines()
	{
	clear();
	class VECTOR tempLines, tempPoints;
	class POLYLINE baseline = GetVectorLine(vectorLine, 1);
	class POLYLINE original;
	class POLYLINE offsetline;
	class RECT extents;
	numeric spacingdist = spacingEN.GetValueNum();
	numeric computedist, i, j, count;
	extents = vectorPolyLayer.Extents;
	VectorToBufferZoneExt(vectorPoly, vectorBuffer, "polygon", buffdistanceEN.GetValueNum(), "meters", "outside");
	ClearVector(temp);
	ClearVector(vectorLine);
	ClearVector(vectorPoint);
	ClearVector(vectorInitLine);
	# Fix orientation line to extend outside polygon.
	# The orientation line needs only to specify directions of lines, but for using as a baseline it needs to intersect with the boundary - increase size of line for this purpose
	class POINT2D pt1, pt2;
	# Determine size to increase line based on polygon extents
	numeric maxdist  = round(ProjDistanceToMeters(crs, extents.x1, extents.y1, extents.x2, extents.y2) / ProjDistanceToMeters(crs, baseline.GetVertex(0).x, baseline.GetVertex(0).y, baseline.GetVertex(1).x, baseline.GetVertex(1).y));
	pt1.x = baseline.GetVertex(0).x + maxdist*(baseline.GetVertex(0).x - baseline.GetVertex(1).x);
	pt1.y = baseline.GetVertex(0).y + maxdist*(baseline.GetVertex(0).y - baseline.GetVertex(1).y);
	pt2.x = baseline.GetVertex(1).x + maxdist*(baseline.GetVertex(1).x - baseline.GetVertex(0).x);
	pt2.y = baseline.GetVertex(1).y + maxdist*(baseline.GetVertex(1).y - baseline.GetVertex(0).y);
	baseline.SetVertex(0, pt1);
	baseline.SetVertex(1, pt2);
	original = baseline;
	# Add baseline as first line
	VectorAddPolyLine(temp, baseline);
	# Start computing offset lines:
	# Do for positive offset direction
		# loop a number of times based on extents of polygon to ensure lines reach end of polygon
	promptFld.SetValueStr("Creating flight lines...");
	for i = 1 to ( round(ProjDistanceToMeters(crs, extents.x1, extents.y1, extents.x2, extents.y2) / spacingdist) + 5) {
		# Convert the distance in meters to degrees
		ComputeDistance(computedist, spacingdist, baseline);
		# Offset the line
		baseline.ComputeOffset(computedist, offsetline);
		# Add the line
		VectorAddPolyLine(temp, offsetline);
		# Next line used is now the new line added
		baseline = offsetline;
		}
	# Reset baseline
	baseline = original;
	# Do for negative offset direction
	for i = 1 to ( round(ProjDistanceToMeters(crs, extents.x1, extents.y1, extents.x2, extents.y2) / spacingdist) + 5) {
		# Convert the distance in meters to degrees
		ComputeDistance(computedist, spacingdist, baseline);
		# Offset the line
		baseline.ComputeOffset(computedist*-1, offsetline);
		# Add the line
		VectorAddPolyLine(temp, offsetline);
		# Next line used is now the new line added
		baseline = offsetline;
		}
	CreateTempVector(tempLines);
	# Get Lines that intersect with buffer for exporting
	vectorInitLine = VectorExtract(vectorBuffer, temp, "PartInside");
	# Clip the Lines to buffer
	promptFld.SetValueStr("Clipping lines to buffer...");
	tempLines = VectorExtract(vectorBuffer, temp, "InsideClip");
	# Add these lines to the vector
	for i = 1 to NumVectorLines(tempLines) {
		class POLYLINE line = GetVectorLine(tempLines, i);
		VectorAddPolyLine(vectorLine, line);
		VectorAddPoint(vectorPoint, line.GetVertex(0).x, line.GetVertex(0).y);
		VectorAddPoint(vectorPoint, line.GetVertex(1).x, line.GetVertex(1).y);
		}
	# Clip the Lines to the Polygon
	promptFld.SetValueStr("Clipping lines to polygon");
	CreateTempVector(tempPoints)
	tempPoints = VectorExtract(vectorPoly, tempLines, "InsideClip");
	for i = 1 to NumVectorLines(tempPoints) {
		class POLYLINE line = GetVectorLine(tempPoints, i);
		VectorAddPoint(vectorPoint, line.GetVertex(0).x, line.GetVertex(0).y);
		VectorAddPoint(vectorPoint, line.GetVertex(1).x, line.GetVertex(1).y);
		}
	CloseVector(tempLines);
	CloseVector(tempPoints);
	LayerDestroy(vectorPointLayer);
	LayerDestroy(vectorLineLayer);
	LayerDestroy(vectorPolyLayer);
	LayerDestroy(vectorBufferLayer);
	vectorPointLayer = GroupQuickAddVectorVar(group, vectorPoint);
	vectorLineLayer = GroupQuickAddVectorVar(group, vectorLine);
	vectorPolyLayer = GroupQuickAddVectorVar(group, vectorPoly);
	vectorBufferLayer = GroupQuickAddVectorVar(group, vectorBuffer);
	ViewRedraw(View);
	promptFld.SetValueStr("Flight lines completed.");
	saveBtnV.SetEnabled(1);
	saveBtnC.SetEnabled(1);
	exportBtn.SetEnabled(1);
	}
# Procedure called when the orientation line is drawn.
proc OnLineToolSet ()
	{
	class TRANSPARM ScreenToView = ViewGetTransViewToScreen(View, 1);
	class TRANSPARM ViewToMap = ViewGetTransMapToView(View, crs, 1);
	coordstart = TransPoint2D(TransPoint2D(LineTool.start, ScreenToView, 0), ViewToMap, 0);
	coordend = TransPoint2D(TransPoint2D(LineTool.end, ScreenToView, 0), ViewToMap, 0);
	# determine degrees for tooltip of orientation line
	numeric degrees = atand((coordend.x - coordstart.x) / (coordend.y - coordstart.y))
	if (coordend.y < coordstart.y)
		degrees = 180 + degrees;
	else if (coordend.x < coordstart.x)
		degrees = 360 + degrees;
	class STRING displaystr = NumToStr(degrees) + " deg";
	class TOOLTIP tooltip;
	tooltip = CreateToolTip(View.DrawingArea, displaystr);
	tooltip.Show();
	promptFld.SetValueStr("Reset line or right-click to accept.");
	}
# Procedure called when user right-clicks to accept the orientation line.
proc OnLineToolApply ()
	{
	array numeric xpoints[5];
	array numeric ypoints[5];
	# clear everything but boundary polygon and buffer polygon
	ClearVector(vectorLine);
	ClearVector(vectorPoint);
	class TRANSPARM ScreenToView = ViewGetTransViewToScreen(View, 1);
	class TRANSPARM ViewToMap = ViewGetTransMapToView(View, crs, 1);
	coordstart = TransPoint2D(TransPoint2D(LineTool.start, ScreenToView, 0), ViewToMap, 0);
	coordend = TransPoint2D(TransPoint2D(LineTool.end, ScreenToView, 0), ViewToMap, 0);
	xpoints[1] = coordstart.x;  xpoints[2] = coordend.x;
	ypoints[1] = coordstart.y;  ypoints[2] = coordend.y;
	# add line
	VectorAddLine(vectorLine, 2, xpoints, ypoints);
	LineTool.HasPosition = 0;
	VectorAddPoint(vectorPoint, coordstart.x, coordstart.y);
	VectorAddPoint(vectorPoint, coordend.x, coordend.y);
	# add line to view
	LayerDestroy(vectorLineLayer);
	LayerDestroy(vectorPointLayer);
	vectorLineLayer = GroupQuickAddVectorVar(group, vectorLine);
	vectorPointLayer = GroupQuickAddVectorVar(group, vectorPoint);
	ViewRedraw(View);
	computeBtn.SetEnabled(1);
	promptFld.SetValueStr("Press Compute Flight Lines button to create flight lines.");
	}
# Procedure called when boundary polygon is drawn in the View.
proc OnPolyLineToolSet()
	{
	promptFld.SetValueStr("Adjust polygon or right-click to accept.");
	}
# Procedure called when user right-clicks to accept the field boundary polygon.
proc OnPolyLineToolApply()
	{
	class POLYLINE polyline;
	polyline = PolyLineTool.GetPolygon();
	polyline.AppendVertex(polyline.GetVertex(0));
	class TRANSPARM ScreenToView = ViewGetTransViewToScreen(View, 1);
	class TRANSPARM ViewToMap = ViewGetTransMapToView(View, crs, 1);
	polyline.ConvertForward(ScreenToView);
	polyline.ConvertForward(ViewToMap);
	VectorAddPolyLine(vectorPoly, polyline);
	PolyLineTool.HasPosition = 0;
	LayerDestroy(vectorPolyLayer);
	vectorPolyLayer = GroupQuickAddVectorVar(group, vectorPoly);
	ViewRedraw(View);
	PolyLineTool.Managed = 0;
	orientationBtn.SetEnabled(1);
	promptFld.SetValueStr("Press Orientation Line button to set direction of flight lines.");
	}
# Procedure called when Create Boundary button on dialog is pressed; activates polyline tool.
proc PolyLineActivate()
	{
	LineTool.Managed = 0;
	PolyLineTool.Managed = 1;
	PolyLineTool.HasPosition = 0;
	promptFld.SetValueStr("Draw polygon in view outlining area boundary.");
	}
# Procedure called when Orientation Line button on dialog is pressed; activates line tool.
proc LineActivate()
	{
	LineTool.Managed = 1;
	LineTool.HasPosition = 0;
	promptFld.SetValueStr("Drag line in view in desired flight line direction.");
	}
# Procedure called when Load Boundary button on dialog is pressed to open a pre-existing
# object for field boundary polygon.
proc OnPolyLineLoad()
	{
	class RVC_OBJITEM objitem;
	DlgGetObject("Select Object", "Vector,CAD,Region", objitem, "ExistingOnly");
	class TRANS2D_MAPGEN trans;
	trans.OutputCoordRefSys = group.ActiveLayer.MapRegion.CoordRefSys;
	numeric i = 0;
	switch (objitem.GetObjectType()) {
	case "VECTOR" :
		class RVC_VECTOR loadedVector;
		loadedVector.Open(objitem);
		class GEOREF georef = GetLastUsedGeorefObject(loadedVector);
		trans.InputCoordRefSys = georef.CoordRefSys;
		for i = 1 to NumVectorLines(loadedVector) {
			class POLYLINE line = GetVectorLine(loadedVector, i);
			line.ConvertForward(trans);
			VectorAddPolyLine(vectorPoly, line);
			}
		CloseVector(loadedVector);
	break;
	case "CAD" :
		class RVC_CAD loadedCAD;
		loadedCAD.Open(objitem);
		class GEOREF georef = GetLastUsedGeorefObject(loadedCAD);
		trans.InputCoordRefSys = georef.CoordRefSys;
		array numeric lineelemlist[100];
		array numeric polyelemlist[100]
		array numeric xPoints[1000];
		array numeric yPoints[1000];
		numeric elem;
		numeric numPoints;
		numeric numLines = CADGetElementList(loadedCAD, 1, "Line", lineelemlist);
		numeric numPolys = CADGetElementList(loadedCAD, 1, "Polygon", polyelemlist);
		for elem = 1 to numLines {
			class POLYLINE line;
			numPoints = CADReadLine(loadedCAD, 1, lineelemlist[elem], xPoints, yPoints);
			for i = 1 to numPoints {
				class POINT2D point;
				point.x = xPoints[i];
				point.y = yPoints[i];
				line.AppendVertex(point);
				}
			line.ConvertForward(trans);
			VectorAddPolyLine(vectorPoly, line);
			}
		for elem = 1 to numPolys {
			class POLYLINE line;
			numPoints = CADReadPoly(loadedCAD, 1, polyelemlist[elem], xPoints, yPoints);
			for i = 1 to numPoints {
				class POINT2D point;
				point.x = xPoints[i];
				point.y = yPoints[i];
				line.AppendVertex(point);
				}
			line.ConvertForward(trans);
			VectorAddPolyLine(vectorPoly, line);
			}
		CloseCAD(loadedCAD);
	break;
	case "REGION":
		class RVC_VECTOR vectorRegion;
		CreateTempVector(vectorRegion);
		class REGION2D loadedRegion;
		ReadRegion(loadedRegion, objitem.GetFilePath(), objitem.GetObjectPath());
		vectorRegion = ConvertRegionToVect(loadedRegion);
		class GEOREF georef = GetLastUsedGeorefObject(vectorRegion);
		trans.InputCoordRefSys = georef.CoordRefSys;
		for i = 1 to NumVectorLines(vectorRegion) {
			class POLYLINE line = GetVectorLine(vectorRegion, i);
			line.ConvertForward(trans);
			VectorAddPolyLine(vectorPoly, line);
			}
		CloseVector(vectorRegion);
	break;
	}
	LayerDestroy(vectorPolyLayer);
	vectorPolyLayer = GroupQuickAddVectorVar(group, vectorPoly);
	ViewRedraw(View);
	orientationBtn.SetEnabled(1);
	promptFld.SetValueStr("Press Orientation Line button to set flight line direction.")
	}
# Procedure called when Export to GPX button on dialog is pressed.
proc OnExport()
	{
	# Exporting points to GPX log
	class SR_COORDREFSYS latloncrs; # coordinate reference system class
	latloncrs.Assign("Geographic2D_WGS84_Deg"); # set crs for vector
	class REGION2D layerregion = group.ActiveLayer.MapRegion;
	layerregion.ConvertTo(latloncrs);
	class RECT extents = layerregion.Extents;
	class TRANSPARM MapToGeog;
	MapToGeog.InputCoordRefSys = group.ActiveLayer.MapRegion.CoordRefSys;
	MapToGeog.OutputCoordRefSys = latloncrs;
	class FILE fOut;
	fOut = GetOutputTextFile("c:/default.txt", "Select Text file", "gpx");
	# write GPX header
	fwritestring(fOut, "<?xml version=\"1.0\"?>\n");
	fwritestring(fOut, "<gpx\n");
	fwritestring(fOut, " version=\"1.0\"\n");
	fwritestring(fOut, " creator=\"MicroImages - https://www.microimages.com\"\n");
	fwritestring(fOut, " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
	fwritestring(fOut, " xmlns=\"http://www.topografix.com/GPX/1/0\"\n");
	fwritestring(fOut, " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n");
	# determine bounding lat/lon
	numeric minlat, minlon, maxlat, maxlon;
	if (extents.x1 < extents.x2) {
		minlon = extents.x1;
		maxlon = extents.x2;
		}
	else {
		minlon = extents.x2;
		maxlon = extents.x1;
		}
	if (extents.y1 < extents.y2) {
		minlat = extents.y1;
		maxlat = extents.y2;
		}
	else {
		minlat = extents.y2;
		maxlat = extents.y1;
		}
	class STRING bounds$ = sprintf("<bounds minlat=\"%.6f\" minlon=\"%.6f\" maxlat=\"%.6f\" maxlon=\"%.6f\"/>\n", minlat, minlon, maxlat, maxlon);
	fwritestring(fOut, bounds$);
	numeric i, j, k;
	class STRINGLIST pointlist;
	class STRING point$, name$;
	class POINT2D pt;
	class GEOREF georef = GetGeorefObject(vectorInitLine);
	for i = 1 to NumVectorLines(vectorInitLine) { # step through each line
		for j = 1 to NumVectorPoints(vectorPoint) { # for all points
			pt.x = vectorPoint.point[j].Internal.x;
			pt.y = vectorPoint.point[j].Internal.y;
			# find points that lie on current line and add these points
			if (FindClosestLine(vectorInitLine, pt.x, pt.y, georef, 0.0001) == i) {
				point$ = sprintf("<wpt lat=\"%.6f\" lon=\"%.6f\">\n", MapToGeog.ConvertPoint2DFwd(pt).y, MapToGeog.ConvertPoint2DFwd(pt).x);
				pointlist.AddToEnd(point$);
				}
			}
	# output points in pointlist
	pointlist.Sort();
	for k = 1 to (pointlist.GetNumItems()) {
		fwritestring(fOut, pointlist[k-1]);
		name$ = sprintf(" <name>LINE%i #%i</name>\n", i, k);
		fwritestring(fOut, name$);
		fwritestring(fOut, "</wpt>\n");
		}
	pointlist.Clear();
	}
	fwritestring(fOut, "</gpx>\n");
	fclose(fOut);
	}
# Procedure called when Save Vector button is pressed.
proc OnSaveVector()
	{
	class RVC_VECTOR VectorOutPoints, VectorOutLines, VectorOutBuffer, VectorOutBoundary;
	class REGION2D layerregion = group.ActiveLayer.MapRegion;
	class RECT layerextents = layerregion.Extents;
	numeric i = 0;
	class STRINGLIST labels;
	labels.AddToEnd("Points Vector");
	labels.AddToEnd("Lines Vector");
	labels.AddToEnd("Buffer Vector");
	labels.AddToEnd("Boundary Vector");
	class RVC_OBJITEM objitemlist[];
	class RVC_DESCRIPTOR descriptor;
	descriptor.SetName("Points");
	objitemlist[1].SetDescriptor(descriptor);
	descriptor.SetName("Lines");
	objitemlist[2].SetDescriptor(descriptor);
	descriptor.SetName("Buffer Polygon");
	objitemlist[3].SetDescriptor(descriptor);
	descriptor.SetName("Boundary Polygon");
	descriptor.SetDescription("Boundary Polygon");
	objitemlist[4].SetDescriptor(descriptor);
	DlgGetObjectSet("Select Vector Object", "Vector", labels, objitemlist, "NewOrExisting");
	# Points
	if (ObjectExists(objitemlist[1].GetFilePath(), objitemlist[1].GetDescriptor().GetShortName(), "Vector") == 0) # check if vector exists
		{
		CreateVector(VectorOutPoints, objitemlist[1].GetFilePath(), objitemlist[1].GetDescriptor().GetShortName(), objitemlist[1].GetDescriptor().GetDescription(), "VectorToolkit", "", layerextents); # create vector
		CreateImpliedGeoref(VectorOutPoints, crs);
		}
	else
		OpenVector(VectorOutPoints, objitemlist[1].GetFilePath(), objitemlist[1].GetDescriptor().GetShortName()); # open vector
	VectorCopyElements(vectorPoint, VectorOutPoints);
	# Lines
	if (ObjectExists(objitemlist[2].GetFilePath(), objitemlist[2].GetDescriptor().GetShortName(), "Vector") == 0) # check if vector exists
		{
		CreateVector(VectorOutLines, objitemlist[2].GetFilePath(), objitemlist[2].GetDescriptor().GetShortName(), objitemlist[2].GetDescriptor().GetDescription(), "VectorToolkit", "", layerextents); # create vector
		CreateImpliedGeoref(VectorOutLines, crs);
		}
	else
		OpenVector(VectorOutLines, objitemlist[2].GetFilePath(), objitemlist[2].GetDescriptor().GetShortName()); # open vector
	VectorCopyElements(vectorLine, VectorOutLines);
	# Polygons
	if (ObjectExists(objitemlist[3].GetFilePath(), objitemlist[3].GetDescriptor().GetShortName(), "Vector") == 0) # check if vector exists
		{
		CreateVector(VectorOutBuffer, objitemlist[3].GetFilePath(), objitemlist[3].GetDescriptor().GetShortName(), objitemlist[3].GetDescriptor().GetDescription(), "VectorToolkit", "", layerextents); # create vector
		CreateImpliedGeoref(VectorOutBuffer, crs);
		}
	else
		OpenVector(VectorOutBuffer, objitemlist[3].GetFilePath(), objitemlist[3].GetDescriptor().GetShortName()); # open vector
	for i = 1 to NumVectorLines(vectorBuffer) {
		class POLYLINE line = GetVectorLine(vectorBuffer, i);
		VectorAddPolyLine(VectorOutBuffer, line);
		}
	if (ObjectExists(objitemlist[4].GetFilePath(), objitemlist[4].GetDescriptor().GetShortName(), "Vector") == 0) # check if vector exists
		{
		CreateVector(VectorOutBoundary, objitemlist[4].GetFilePath(), objitemlist[4].GetDescriptor().GetShortName(), objitemlist[4].GetDescriptor().GetDescription(), "VectorToolkit", "", layerextents); # create vector
		CreateImpliedGeoref(VectorOutBoundary, crs);
		}
	else
		OpenVector(VectorOutBoundary, objitemlist[4].GetFilePath(), objitemlist[4].GetDescriptor().GetShortName()); # open vector
	for i = 1 to NumVectorLines(vectorPoly) {
		class POLYLINE line = GetVectorLine(vectorPoly, i);
		VectorAddPolyLine(VectorOutBoundary, line);
		}
	CloseVector(VectorOutPoints);
	CloseVector(VectorOutLines);
	CloseVector(VectorOutBuffer);
	CloseVector(VectorOutBoundary);
	} # end OnSaveVector()
# Procedure called when Save CAD button is pressed.
proc OnSaveCAD()
	{
	class RVC_CAD CADOutPoints, CADOutLines, CADOutBuffer, CADOutBoundary;
	numeric i = 0;
	class STRINGLIST labels;
	labels.AddToEnd("Points CAD");
	labels.AddToEnd("Lines CAD");
	labels.AddToEnd("Buffer CAD");
	labels.AddToEnd("Boundary CAD");
	class RVC_OBJITEM objitemlistCAD[];
	class RVC_DESCRIPTOR descriptor;
	descriptor.SetName("Points");
	objitemlistCAD[1].SetDescriptor(descriptor);
	descriptor.SetName("Lines");
	objitemlistCAD[2].SetDescriptor(descriptor);
	descriptor.SetName("Buffer Polygon");
	objitemlistCAD[3].SetDescriptor(descriptor);
	descriptor.SetName("Boundary Polygon");
	descriptor.SetDescription("Boundary Polygon");
	objitemlistCAD[4].SetDescriptor(descriptor);
	DlgGetObjectSet("Select CAD Object", "CAD", labels, objitemlistCAD, "NewOrExisting");
	# Points
	if (ObjectExists(objitemlistCAD[1].GetFilePath(), objitemlistCAD[1].GetDescriptor().GetShortName(), "CAD") == 0) # check if vector exists
		{
		CreateCAD(CADOutPoints, objitemlistCAD[1].GetFilePath(), objitemlistCAD[1].GetDescriptor().GetShortName(), objitemlistCAD[1].GetDescriptor().GetDescription()); # create vector
		CreateImpliedGeoref(CADOutPoints, crs);
		CADCreateBlock(CADOutPoints, "Points", "PointsBlock");
		}
	else
		OpenCAD(CADOutPoints, objitemlistCAD[1].GetFilePath(), objitemlistCAD[1].GetDescriptor().GetShortName()); # open vector
	numeric i = 1;
	numeric j = 1;
	for i = 1 to NumVectorPoints(vectorPoint)
		CADWritePoint(CADOutPoints, 1, vectorPoint.point[i].Internal.x, vectorPoint.point[i].Internal.y);
	# Lines
	if (ObjectExists(objitemlistCAD[2].GetFilePath(), objitemlistCAD[2].GetDescriptor().GetShortName(), "CAD") == 0) # check if vector exists
		{
		CreateCAD(CADOutLines, objitemlistCAD[2].GetFilePath(), objitemlistCAD[2].GetDescriptor().GetShortName(), objitemlistCAD[2].GetDescriptor().GetDescription()); # create vector
		CreateImpliedGeoref(CADOutLines, crs);
		CADCreateBlock(CADOutLines, "Lines", "LinesBlock");
		}
	else
		OpenCAD(CADOutLines, objitemlistCAD[2].GetFilePath(), objitemlistCAD[2].GetDescriptor().GetShortName()); # open vector
	for i = 1 to NumVectorLines(vectorLine) {
		class POLYLINE vline = GetVectorLine(vectorLine, i);
		array xpoints[vline.GetNumPoints()];
		array ypoints[vline.GetNumPoints()];
		for j = 1 to vline.GetNumPoints() {
			xpoints[j] = vline.GetVertex(j-1).x;
			ypoints[j]= vline.GetVertex(j-1).y
			}
		CADWriteLine(CADOutLines, 1, vline.GetNumPoints(), xpoints, ypoints);
		}
	# Polygons
	if (ObjectExists(objitemlistCAD[3].GetFilePath(), objitemlistCAD[3].GetDescriptor().GetShortName(), "CAD") == 0) # check if vector exists
		{
		CreateCAD(CADOutBuffer, objitemlistCAD[3].GetFilePath(), objitemlistCAD[3].GetDescriptor().GetShortName(), objitemlistCAD[3].GetDescriptor().GetDescription()); # create vector
		CreateImpliedGeoref(CADOutBuffer, crs);
		CADCreateBlock(CADOutBuffer, "Polygons", "PolygonBlock");
		}
	else
		OpenCAD(CADOutBuffer, objitemlistCAD[3].GetFilePath(), objitemlistCAD[3].GetDescriptor().GetShortName()); # open vector
	for i = 1 to NumVectorLines(vectorBuffer) {
		class POLYLINE vline = GetVectorLine(vectorBuffer, i);
		array xpoints[vline.GetNumPoints()];
		array ypoints[vline.GetNumPoints()];
		for j = 1 to vline.GetNumPoints() {
			xpoints[j] = vline.GetVertex(j-1).x;
			ypoints[j]= vline.GetVertex(j-1).y;
			}
		CADWriteLine(CADOutBuffer, 1, vline.GetNumPoints(), xpoints, ypoints);
		}
	if (ObjectExists(objitemlistCAD[4].GetFilePath(), objitemlistCAD[4].GetDescriptor().GetShortName(), "CAD") == 0) # check if vector exists
		{
		CreateCAD(CADOutBoundary, objitemlistCAD[4].GetFilePath(), objitemlistCAD[4].GetDescriptor().GetShortName(), objitemlistCAD[4].GetDescriptor().GetDescription()); # create vector
		CreateImpliedGeoref(CADOutBoundary, crs);
		CADCreateBlock(CADOutBoundary, "Polygons", "PolygonBlock");
		}
	else
		OpenCAD(CADOutBoundary, objitemlistCAD[4].GetFilePath(), objitemlistCAD[4].GetDescriptor().GetShortName()); # open vector
	for i = 1 to NumVectorLines(vectorPoly) {
		class POLYLINE vline = GetVectorLine(vectorPoly, i);
		array xpoints[vline.GetNumPoints()];
		array ypoints[vline.GetNumPoints()];
		for j = 1 to vline.GetNumPoints() {
			xpoints[j] = vline.GetVertex(j-1).x;
			ypoints[j]= vline.GetVertex(j-1).y;
			}
		CADWriteLine(CADOutBoundary, 1, vline.GetNumPoints(), xpoints, ypoints);
		}
	CloseCAD(CADOutPoints);
	CloseCAD(CADOutLines);
	CloseCAD(CADOutBuffer);
	CloseCAD(CADOutBoundary);
	}	# end OnSaveCAD()
# procedure called when Close button is pressed
proc OnClose()
	{
	PolyLineTool.Managed = 0;
	LineTool.Managed = 0;
	CloseVector(vectorInitLine);
	CloseVector(temp);
	dlgwin.Close(1);
	View.SetDefaultTool();
	}
# function called when script is initialized.  Use to set up custom dialog window.
func OnInitialize ()
	{
	if (Layout) {
		group = Layout.ActiveGroup;
		}
	else
		group = Group;
	local numeric errXML;
	local class XMLDOC dlgdoc;
	local class XMLNODE dlgnode;
	local string xml$ = '<?xml version="1.0"?>
	<root>
		<dialog id= "flightplan" Title="Flight Plan" Buttons="">
			<pane Orientation="horizontal">
				<label>Line Spacing (m)</label>
				<editnumber id="spacingEN" MinVal="0" HorizResize="expand" Default="0" Precision="1" OnUserEdit="OnSpacing()" />
				<label id="bufflabel" Enabled="false">Buffer Distance (m)</label>
				<editnumber id="buffdistanceEN" MinVal="0" HorizResize="expand" Enabled="false" Default="0" Precision="1" OnChanged="OnBuffDistance()"/>
			</pane>
			<pane Orientation="horizontal">
				<pushbutton id="boundaryBtn" Name="Create Boundary" WidthGroup="1" HorizResize="expand" Enabled="false" OnPressed="PolyLineActivate()"/>
				<pushbutton id="loadboundaryBtn" Name="Load Boundary" WidthGroup="2" HorizResize="expand" Enabled="false" OnPressed="OnPolyLineLoad()"/>
			</pane>
			<pane Orientation="horizontal">
				<pushbutton id="orientationBtn" Name="Orientation Line" WidthGroup="1" HorizResize="expand" Enabled="false" OnPressed="LineActivate()"/>
				<pushbutton id="computeBtn" Name="Compute Flight Lines" WidthGroup="2" HorizResize="expand" Enabled="false" OnPressed="ComputeLines()"/>
			</pane>
			<pane Orientation="horizontal">
				<pushbutton id="saveBtnV" Name="Save Vector" WidthGroup="1" HorizResize="expand" Enabled="false" OnPressed="OnSaveVector()"/>
				<pushbutton id="saveBtnC" Name="Save CAD" WidthGroup="2" HorizResize="expand" Enabled="false" OnPressed="OnSaveCAD()"/>
			</pane>
			<pane Orientation="horizontal">
				<pushbutton id="exportBtn" Name="Export to GPX" WidthGroup="1" HorizResize="expand" Enabled="false" OnPressed="OnExport()"/>
				<pushbutton id="closeBtn" Name="Close" WidthGroup="2" HorizResize="expand" OnPressed="OnClose()"/>
			</pane>
			<edittext id="promptFld" HorizResize="expand" ReadOnly="true" />
		</dialog>
	</root>';
	################################################################
	### parse XML text for main search dialog into memory;
	### return an error code (number < 0) if there are syntax errors
	################################################################
	errXML = dlgdoc.Parse(xml$);
	if (errXML < 0)
		{
		PopupError(errXML);
		Exit();
		}
	##########################################################
	# get the dialog element from the parsed XML document and
	# show error message if the dialog element can't be found
	##########################################################
	dlgnode = dlgdoc.GetElementByID("flightplan");
	if (dlgnode == 0)
		{
		PopupMessage("Could not find dialog node in XML document");
		Exit();
		}
	##########################################################
	# Set the XML dialog element as the source for the GUI_DLG
	# class instance we are using for the dialog window.
	##########################################################
	dlgwin.SetXMLNode(dlgnode);
	dlgwin.CreateModeless();		### create as modeless dialog
	################################################################################
	### get handles for dialog controls using their ID's in the dialog specification
	################################################################################
	spacingEN = dlgwin.GetCtrlByID("spacingEN");
	bufflabel = dlgwin.GetCtrlByID("bufflabel");
	buffdistanceEN = dlgwin.GetCtrlByID("buffdistanceEN");
	orientationBtn = dlgwin.GetCtrlByID("orientationBtn");
	boundaryBtn = dlgwin.GetCtrlByID("boundaryBtn");
	loadboundaryBtn = dlgwin.GetCtrlByID("loadboundaryBtn");
	computeBtn = dlgwin.GetCtrlByID("computeBtn");
	saveBtnV = dlgwin.GetCtrlByID("saveBtnV");
	saveBtnC = dlgwin.GetCtrlByID("saveBtnC");
	exportBtn = dlgwin.GetCtrlByID("exportBtn");
	closeBtn = dlgwin.GetCtrlByID("closeBtn");
	promptFld = dlgwin.GetCtrlByID("promptFld");
	##### Setup Vectors
	crs = group.ActiveLayer.MapRegion.CoordRefSys;
	class REGION2D layerregion;
	layerregion = group.ActiveLayer.MapRegion;
	class RECT layerextents = layerregion.Extents;
	CreateTempVector(vectorPoint, "VectorToolkit", "", layerextents);
	CreateImpliedGeoref(vectorPoint, crs);
	CreateTempVector(vectorLine, "VectorToolkit", "", layerextents);
	CreateImpliedGeoref(vectorLine, crs);
	CreateTempVector(vectorInitLine, "VectorToolkit", "", layerextents);
	CreateImpliedGeoref(vectorInitLine, crs);
	CreateTempVector(vectorPoly, "", "", layerextents);
	CreateImpliedGeoref(vectorPoly, crs);
	CreateTempVector(vectorBuffer, "VectorToolkit", "", layerextents);
	CreateImpliedGeoref(vectorBuffer, crs);
	CreateTempVector(temp, "", "", layerextents);
	CreateImpliedGeoref(temp, crs);
	#####
	# Add point tool to view.
	#LineTool = ViewCreateLineTool(View,"Line Tool","add_line","TNTedit");
	LineTool = ViewCreateLineGadget(View);
	ToolAddCallback(LineTool.ApplyCallback,OnLineToolApply);
	ToolAddCallback(LineTool.PositionSetCallback,OnLineToolSet);
	LineTool.DialogPosition = "RightCenter";
	#PolyLineTool = ViewCreatePolyLineTool(View, "Region Tool", "add_polygon", "TNTedit");
	PolyLineTool = ViewCreatePolyLineGadget(View);
	ToolAddCallback(PolyLineTool.ApplyCallback, OnPolyLineToolApply);
	ToolAddCallback(PolyLineTool.PositionSetCallback, OnPolyLineToolSet);
	View.ShowDataTips = 0;
	}	# end of OnInitialize()
# function called whenever the tool is activated (initially and after switching tools).
func OnActivate ()
	{
	PolyLineTool.Managed = 0;
	LineTool.Managed = 0;
	dlgwin.Open();
	spacingEN.SetFocus();
	promptFld.SetValueStr("Enter flight line spacing in meters.");
	}
# function called whenever the tool is deactivated (as by switching tools).
func OnDeactivate () {
	PolyLineTool.Managed = 0;
	LineTool.Managed = 0;
	dlgwin.Close(0);		# close dialog without notifying, so OnClose() callback is not
	}							# called.  Allows user to switch tools and come back to script dialog.