TNTmips

HOME

FREE PRODUCTS
  TNTlite
  TNTatlas
  TNTsim3D

DOWNLOADS
  Release Version
  Development Version
  FTP
  Language Kits
  Sample Geodata
  Reseller Resources
  Promotional

DOCUMENTATION
  Tutorials
  Technical Guides
  Quick Guides

SITE MAP


PipeProfileCAD.sml


# PipeProfileCAD.sml
#
# This script is meant to be run as a Toolscript
#
# Current Assumptions:
# In the group there are two layers:
# Layer 1: DEM - not needed
# Layer 2: Overlaying vector lines (must be last layer)
#
# Purpose:
# The script creates a window displaying a plotted profile of the
# selected lines nearest line, using the vector DB values for elevation.
# Only allows selection of contiguous connected lines, but can add to
# either end of selected line set.  Active elements are highlighted in
# both the View and Line Profile window on mouseover in either window.
#
# Usage:
# Left click: toggle select/deselect line
# Right click: Accept current line and draw

# Save As button on Line Profile window saves the current graph to a CAD object.
#
# Requires TNTmips 7.0 dated 1 March 2005 or later.

## MicroImages, Inc.

## Revised 19 April 2005. Fixed inconsistency in placement of graph axis labels in
## Line Profile window and CAD. 

# GUI global definitions
class GUI_DLG dlgwin;
class GUI_CANVAS canvas;
class GC gc;			# global graphics context for drawing canvas in dialog
class GRDEVICE_CAD deviceCAD;
class GUI_CTRL_TOGGLEBUTTON gridToggle;
class GUI_CTRL_EDIT_NUMBER gridIntervalX;
class GUI_CTRL_EDIT_NUMBER gridIntervalY;
class GUI_CTRL_TOGGLEBUTTON demToggle;
class GUI_CTRL_EDIT_NUMBER graphMinSetting;
class GUI_CTRL_EDIT_NUMBER graphMaxSetting;
class GUI_CTRL_TOGGLEBUTTON fillToggle;
#class GUI_CTRL_EDIT_NUMBER elemDisplay;
#class GUI_CTRL_EDIT_NUMBER lineDisplay;
#class GUI_CTRL_EDIT_NUMBER vertexDisplay;
class GUI_CTRL_EDIT_NUMBER mouseXDisplay;
class GUI_CTRL_EDIT_NUMBER mouseYDisplay;
class GUI_CTRL_EDIT_NUMBER slopeDisplay;
class GUI_CTRL_EDIT_NUMBER diameterDisplay;
class GUI_CTRL_EDIT_STRING materialDisplay;

# Group/layer/object global definitions
class GRE_GROUP activegroup;
class TRANSPARM mapTrans;
class GRE_LAYER_VECTOR vectorLayer;
class GRE_LAYER_RASTER rasterLayer;
class VECTOR lineVector;
class GEOREF vecGeoref;
class RASTER dem;
string vectorName$;
class DBTABLEINFO nodeTable;
class DBTABLEINFO lineTable;
numeric dbIsInit=0;
class CAD CADgraph;

# Graphical display global definitions
class POLYLINE pipeBottom, pipeTop, pipeFace, surface;
class POLYLINE surface, manholeDepth, demSurface, smoothedSurface;
class STRINGLIST manholeNames;

class POLYLINE pipeBottomSave, pipeTopSave, pipeFaceSave; # used for highlighting
class STRINGLIST colors;
numeric drawable=0, demSet=0;
numeric graphMinZ, graphMaxZ, dataMinZ, dataMaxZ;
numeric setDefaultWhenClose = false;
numeric leftGraphOffset, bottomGraphOffset=20, rightGraphOffset=15, topGraphOffset=50; #5,15
numeric currentlyActive = -1;
numeric needsRedraw = 0;

# Utility global definitions
class POLYLINE demLine;
class STRINGLIST elemList;
class STRINGLIST orderedElemList;
class STRINGLIST reversedList;
array numeric endNodes[2];
numeric MAX_NUMBER = 9999999;

# 
func writePolyline(class FILE f, class POLYLINE polyline)
{
	local class POINT2D p2d;
	local numeric i;
	for (i=0; i

func assignColors()
{
	colors.AddToEnd("red");
	colors.AddToEnd("orange");
	colors.AddToEnd("yellow");
	colors.AddToEnd("green");
	colors.AddToEnd("blue");
	colors.AddToEnd("purple");
	colors.AddToEnd("violet");
}


func class COLOR getColor(numeric index)
{
	local class COLOR c;
	c.Name = colors.GetString(index%colors.GetNumItems()-1);
	return c;
}


# Determine if the dem will be used or not
func doUseDEM()
{
	return demToggle.GetValue();
}

# Get the line table by name - return 1 if successful, 0 if table is null
func initLineTable(string tablename)
{
	local class DATABASE lineDB = OpenVectorLineDatabase(lineVector);
	if (lineDB==0) return 0;
	lineTable = DatabaseGetTableInfo(lineDB, tablename);
	if (lineTable==0) return 0;
	return 1;
}

# Get the node table by name - return 1 if successful, 0 if table is null
func initNodeTable(string tablename)
{
	local class DATABASE nodeDB = OpenVectorNodeDatabase(lineVector);
	if (nodeDB==0) return 0;
	nodeTable = DatabaseGetTableInfo(nodeDB, tablename);
	if (nodeTable==0) return 0;
	return 1;
}

# Checks layer to see if it is valid.
func checkLayer()
{
	if (doUseDEM())
	{
		# Raster is assumed to be first layer
		rasterLayer = activegroup.FirstLayer;
		if (rasterLayer.Type!="Raster")
		{
			demSet = 0;
			PopupMessage("The first layer is not a raster, the DEM was not assigned properly and the script may not function properly.");
		}
		DispGetRasterFromLayer(dem, rasterLayer);
		demSet = 1;
	}

	# Vector is assumed to be last layer
	vectorLayer = activegroup.LastLayer;
	DispGetVectorFromLayer(lineVector, vectorLayer);
	vecGeoref = GetLastUsedGeorefObject(lineVector);

	mapTrans.InputProjection = vectorLayer.Projection;
	mapTrans.OutputProjection = rasterLayer.Projection;

	# initialize the database tables
	initLineTable("SEW_INFO");
	initNodeTable("MH_GIS");

	return 1;
}

# Callback for when the active layer changes.
proc cbLayer()
{
	checkLayer();
}

# Callback for when the active group changes.
proc cbGroup()
{
	activegroup = Layout.ActiveGroup;
	WidgetAddCallback(activegroup.LayerSelectedCallback, cbLayer);
	cbLayer();
}

# return the max of two numbers
func max(numeric n1, numeric n2)
{
	if (n1 > n2) return n1;
	return n2;
}

# Get the element from our list
func getElement(class STRINGLIST list, numeric index)
{
	return StrToNum(list.GetString(index));
}

# Get the index of the element in our list, return index if found, -1 otherwise
func indexOf(class STRINGLIST list, numeric value)
{
	local numeric i;
	for (i=0; i-1)
#	{
#		local class POINT2D point1 = line.GetVertex(vertexNum);
#		local class POINT2D point2 = line.GetVertex(vertexNum+1);
#
#		if (point1!=point2)
#		{
#			return 0;
#		}
#	}
#	return 1;
#}


# Compute the distance between two points
func computeDistance(class POINT2D p1, class POINT2D p2)
{
	return sqrt((p2.x-p1.x)^2 + (p2.y-p1.y)^2);
}

# Determine if the given lin, col is within the raster extents
func isPointInRaster(numeric lin, numeric col)
{
	if (lin<1 || col<1 || lin>NumLins(dem) || col>NumCols(dem)) return 0;
	return 1;
}

# Get the value of the first record attached to the node "nodeNum" as a string
func string readNodeTableRecordStr(numeric nodeNum, string field)
{
	local array numeric records[1];
	TableReadAttachment(nodeTable, nodeNum, records, "node");
	local string retVal = TableReadFieldStr(nodeTable, field, records[1]);
	return retVal;
}

# Get the value of the first record attached to the line "lineNum" as a string
func string readLineTableRecordStr(numeric lineNum, string field)
{
	local array numeric records[1];
	TableReadAttachment(lineTable, lineNum, records, "line");
	local string retVal = TableReadFieldStr(lineTable, field, records[1]);
	return retVal;
}

# Get the value of the first record attached to the line "lineNum"
func readLineTableRecord(numeric lineNum, string field)
{
	local array numeric records[1];
	TableReadAttachment(lineTable, lineNum, records, "line");
	local numeric retVal = TableReadFieldNum(lineTable, field, records[1]);
	if (IsNull(retVal)) return 0;
	return retVal;
}

# Get the value of the first record attached to the line "nodeNum"
func readNodeTableRecord(numeric nodeNum, string field)
{
	local array numeric records[1];
	TableReadAttachment(nodeTable, nodeNum, records, "node");
	local numeric retVal = TableReadFieldNum(nodeTable, field, records[1]);
	return retVal;
}

# Get the z value
func computeElevationFromDEM(class POINT2D p)
{
	p = mapTrans.ConvertPoint2DFwd(p);	# convert from vector map to raster map coords

	local class POINT2D obj = MapToObject(GetLastUsedGeorefObject(dem), p.x, p.y, dem);
	obj.x = obj.x + .5; # center of cell
	obj.y = obj.y + .5; # center of cell
	local numeric demValue;
	if(isPointInRaster(obj.y, obj.x)) demValue = dem[obj.y, obj.x];	# y for line, x for column
	else demValue = null;
	return demValue;
}

# Find the closest line element using the polyline and the two nodes (vertices)
func findClosestLineElement(class POLYLINE origLine, numeric vertex1, numeric vertex2)
{
	# get the line given the two end points - straight line assumed
	local class POINT2D point1 = origLine.GetVertex(vertex1);
	local class POINT2D point2 = origLine.GetVertex(vertex2);
	local class POINT2D midPt = point1;
	midPt = midPt + point2;
	midPt.x = midPt.x / 2;
	midPt.y = midPt.y / 2;
	local numeric elemNum = FindClosestLine(lineVector, midPt.x, midPt.y);
	return elemNum;
}

# Interpolate between two points adding extra vertices
func class POLYLINE constructSmoothSurface(class POLYLINE p, numeric linscale, numeric colscale, numeric samplingRate)
{
	local class POLYLINE tmpLine, retLine;
	local class POINT2D point1, point2, tmpPt;
	local numeric numIntervals = 3;

	local numeric i;
	for (i=1; i graphMaxZ);
#	return (value == NullValue(dem));
}

# Get the width from the appropriate drawing device
func getHeight()
{
	return canvas.GetHeight();
}

# Get the width from the appropriate drawing device
func getWidth()
{
	return canvas.GetWidth();
}

# Get the extents of the graph
# (x1, y1) == UL corner
# (x2, y2) == LR corner
func class RECT getGraphExtents()
{
	local class RECT rect;
	rect.x1 = leftGraphOffset;
	rect.x2 = getWidth() - rightGraphOffset;
	rect.y1 = topGraphOffset;
	rect.y2 = getHeight() - bottomGraphOffset;
	return rect;
}

# Set the affine transformation for distance/elevation -> Graph Coords
proc setTrans(class POLYLINE graphLine)
{
	# copy the point
#	local class POINT2D retpoint;
#	retpoint = point;
#
#	# get graph extents
#	local numeric minx, maxx, miny, maxy;
#	minx = leftGraphOffset;
#	miny = topGraphOffset;
#	maxx = getWidth() - rightGraphOffset;
#	maxy = getHeight() - bottomGraphOffset;
#
#	# Get the drawing scale
#	local numeric xscale = 0, yscale = 0;
#	xscale = (maxx - minx) / abs(graphLine.GetVertex(graphLine.GetNumPoints()-1).x - graphLine.GetVertex(0).x);
#	if (graphMaxZ != graphMinZ) yscale = (maxy - miny) / (graphMaxZ - graphMinZ);
#
#	# apply scales and offsets
#	retpoint.x = point.x * xscale + leftGraphOffset;
#	if (yscale!=0) retpoint.y = getHeight() - ((point.y-graphMinZ) * yscale + bottomGraphOffset);
#	else retpoint.y = getHeight() - bottomGraphOffset;

	# redefinition here clears the trans each time we draw (which is desirable)
	class TRANSAFFINE trans;
	trans.ApplyScale(1,1);
	trans.ApplyOffset(0,0);

	# get graph extents
	local numeric minx, maxx, miny, maxy;
	minx = leftGraphOffset;
	miny = topGraphOffset;
	maxx = getWidth() - rightGraphOffset;
	maxy = getHeight() - bottomGraphOffset;

	# Get the drawing scale
	local numeric xscale = 0, yscale = 0;
	xscale = (maxx - minx) / abs(graphLine.GetVertex(graphLine.GetNumPoints()-1).x - graphLine.GetVertex(0).x);
	if (graphMaxZ != graphMinZ) yscale = (maxy - miny) / (graphMaxZ - graphMinZ);

	# apply scales and offsets
	trans.ApplyScale(xscale, -yscale);
	trans.ApplyOffset(leftGraphOffset, getHeight() + graphMinZ*yscale - bottomGraphOffset);
}

# Create the GC here using the appropriate drawing device (gc is global)
proc createGC()
{
	gc = canvas.CreateGC();
}

# draw the graph background fill
proc drawBackground(class GC gc, class COLOR bgcolor)
{
	if (drawable)
	{
		# draw the background rectangle
		gc.SetColorRGB(bgcolor.red, bgcolor.green, bgcolor.blue, 100);
		gc.FillRect(0, 0, getWidth(), getHeight());
	}
}

# draw the axes for the graph - dataMinZ == MAX_NUMBER and dataMaxZ == -MAX_NUMBER are displayed as null
proc drawGraphAxes(class GC gc, numeric distance, string xAxisLabel, string yAxisLabel, numeric drawTwoPointLines, class COLOR axiscolor, numeric fontHeight, numeric axisLabelOffset)
{
	if (drawable)
	{
		# set the axis colors
		gc.SetColorRGB(axiscolor.red, axiscolor.green, axiscolor.blue, 100);

		# set the min and max display variables
		local string min$ = sprintf("%0.2f", graphMinZ);
		local string max$ = sprintf("%0.2f", graphMaxZ);
		if (graphMaxZ==-MAX_NUMBER) max$ = "null";
		if (graphMinZ==MAX_NUMBER) min$ = "null";

		# Draw text for coordinate and elevation axis labels
		gc.DrawTextSetFont("ARIAL");
		gc.DrawTextSetHeightPixels(fontHeight);

		# Draw graph axes
		if (graphMaxZ == graphMinZ) max$ = "";
		local array numeric graphx[3], graphy[3];
		graphx[1] = leftGraphOffset;
		graphy[1] = topGraphOffset;
		graphx[2] = leftGraphOffset;
		graphy[2] = getHeight() - bottomGraphOffset;
		graphx[3] = getWidth() - rightGraphOffset;
		graphy[3] = graphy[2];
		gc.DrawPolyLine(graphx, graphy, 3);

		# draw y axis labels
		gc.TextStyle.Smoothing = 1;
		gc.DrawTextSimple(max$, 2, graphy[1]+fontHeight/2);
		gc.DrawTextSimple(min$, 2, graphy[2]+fontHeight/2);
		gc.DrawTextSimple(yAxisLabel, graphx[1]-axisLabelOffset, (graphy[3]-graphy[1])/2+gc.TextGetWidth(yAxisLabel)*1.5, 90);

		# draw x axis labels
		gc.DrawTextSimple("0", graphx[1] - gc.TextGetWidth("0")/2, getHeight()-3);
		local string str$ = sprintf("%0.2f", distance);
		gc.DrawTextSimple(str$, getWidth() - gc.TextGetWidth(str$), getHeight()-3);
		gc.DrawTextSimple(xAxisLabel, (graphx[3]-graphx[1])/2-gc.TextGetWidth(xAxisLabel)/4, graphy[3]+fontHeight+axisLabelOffset);
	}
}

# Translate a point on the graphline to image device coordinates for drawing
func class POINT2D transPointToGraph(class POINT2D point, class POLYLINE graphLine)
{
	local class POINT2D retpoint;
	if (trans!=0) retpoint = trans.ConvertPoint2DFwd(point);

	# copy the point
#	local class POINT2D retpoint;
#	retpoint = point;
#
#	# get graph extents
#	local numeric minx, maxx, miny, maxy;
#	minx = leftGraphOffset;
#	miny = topGraphOffset;
#	maxx = getWidth() - rightGraphOffset;
#	maxy = getHeight() - bottomGraphOffset;
#
#	# Get the drawing scale
#	local numeric xscale = 0, yscale = 0;
#	xscale = (maxx - minx) / abs(graphLine.GetVertex(graphLine.GetNumPoints()-1).x - graphLine.GetVertex(0).x);
#	if (graphMaxZ != graphMinZ) yscale = (maxy - miny) / (graphMaxZ - graphMinZ);
#
#	# apply scales and offsets
#	retpoint.x = point.x * xscale + leftGraphOffset;
#	if (yscale!=0) retpoint.y = getHeight() - ((point.y-graphMinZ) * yscale + bottomGraphOffset);
#	else retpoint.y = getHeight() - bottomGraphOffset;

	return retpoint;
}

# Plot the vertex and any connecting lines in the graphLine
# (note: this method is a bit more clever as it doesn't translate a point more than once
# but is not being used currently - it does make drawPolyline a bit less clear however).
proc plotLine(class GC gc, class POLYLINE graphLine, numeric vertexNum)
{
	local class POINT2D linePoint, graphPoint;

	linePoint = graphLine.GetVertex(vertexNum);
	graphPoint = transPointToGraph(linePoint, graphLine);

	if (0)#!isConsecutive(graphLine, vertexNum))
	{
		# if not consecutive, finish drawing and move to next
		gc.DrawTo(graphPoint.x, graphPoint.y);
		linePoint = graphLine.GetVertex(vertexNum+1);
		graphPoint = transPointToGraph(linePoint, graphLine);
		gc.MoveTo(graphPoint.x, graphPoint.y);
	}
	else if (isNull(linePoint.y))
	{
		# if null skip point and move to next
		linePoint = graphLine.GetVertex(vertexNum+1);
		graphPoint = transPointToGraph(linePoint, graphLine);
		gc.MoveTo(graphPoint.x, graphPoint.y);
	}
	else
	{
		gc.DrawTo(graphPoint.x, graphPoint.y);
	}
}

# draw a two point line from the polyline p
proc drawLineSegment(class POLYLINE p, numeric vertex1, numeric vertex2, class COLOR color)
{
	local class POINT2D point1, point2;
	point1 = p.GetVertex(vertex1);
	point2 = p.GetVertex(vertex2);

	if (isNull(point1.y)||isNull(point2.y))
	{
		# if null skip point and move to next
		point2 = transPointToGraph(point2, p);
		gc.MoveTo(point2.x, point2.y);
	}
	else
	{
		point1 = transPointToGraph(point1, p);
		point2 = transPointToGraph(point2, p);
		gc.SetColorRGB(color.red, color.green, color.blue, 100);
		gc.MoveTo(point1.x, point1.y);
		gc.DrawTo(point2.x, point2.y);
	}
}


# Draw the graph with the given polyline (not used currently - use with plotLine)
proc drawPolyline2(class GC gc, class POLYLINE graphLine, class COLOR lineColor)
{
	if (drawable && graphLine.GetNumPoints()>0)
	{
		# Draw the profile
		gc.SetColorRGB(lineColor.red, lineColor.green, lineColor.blue, 100);
		local class POINT2D linePoint = graphLine.GetVertex(0);

		# Plot point zero
		local class POINT2D graphPoint = transPointToGraph(linePoint, graphLine);
		gc.DrawPoint(graphPoint.x, graphPoint.y);

		# Plot the rest of the points - connecting them as lines
		local numeric i;
		for (i=0; i0)
	{
		# Plot the polyline one segment at a time
		local numeric i;
		for (i=0; i=5)
	{
		local numeric i;
		for (i=0; i0)
	{
		gc.DrawTextSetFont(font);
		gc.DrawTextSetHeightPixels(pixelFontHeight);
		gc.DrawTextSetColors(textColor);

		# Draw all the manhole names at the manhole tops
		local numeric i;
		for (i=0; i0 && surface.GetNumPoints()>0)
	{
		# Draw the profile
		gc.SetColorRGB(lineColor.red, lineColor.green, lineColor.blue, 100);

		# Plot the rest of the points - connecting them as lines
		local numeric i;
		for (i=0; i1)
	{
		# set the grid color
		gc.SetColorRGB(color.red, color.green, color.blue, 100);

		local class POINT2D pt1, pt2;

		# draw vertical lines
		local numeric length = bottomLine.GetVertex(bottomLine.GetNumPoints()-1).x;
		local class POINT2D bottomPoint;
		local class POINT2D topPoint;
		bottomPoint.y = graphMinZ;
		topPoint.y = graphMaxZ;
		for (bottomPoint.x = topPoint.x = xspacing; bottomPoint.x<=length; topPoint.x = bottomPoint.x = bottomPoint.x + xspacing)
		{
			# get graph coordinates
			local class POINT2D graphBottomPoint = transPointToGraph(bottomPoint, bottomLine);
			local class POINT2D graphTopPoint = transPointToGraph(topPoint, bottomLine);

			# do the drawing
			gc.MoveTo(graphTopPoint.x, graphTopPoint.y);
			gc.DrawTo(graphBottomPoint.x, graphBottomPoint.y);
		}

		# draw horizontal lines
		bottomPoint.x = 0;
		topPoint.x = length;
		bottomPoint.y = topPoint.y = graphMinZ;
		for (bottomPoint.y = topPoint.y = ceil(graphMinZ); bottomPoint.y<=graphMaxZ; topPoint.y = bottomPoint.y = bottomPoint.y + yspacing)
		{
			# get graph coordinates
			local class POINT2D graphBottomPoint = transPointToGraph(bottomPoint, bottomLine);
			local class POINT2D graphTopPoint = transPointToGraph(topPoint, bottomLine);

			# do the drawing
			gc.MoveTo(graphTopPoint.x, graphTopPoint.y);
			gc.DrawTo(graphBottomPoint.x, graphBottomPoint.y);
		}
	}
}

# Convert the polyline from obj to map coordinates
func class POLYLINE convertObjectToMap(class POLYLINE line)
{
	local class POLYLINE ret;
	local class POINT2D obj, map;

	local numeric i;

	# loop through the lines and convert from object to map coords
	for (i=0; i2) polyline.Straighten();
	}
	# if this is the first line just append and return
	if (line.GetNumPoints()==0)
	{
		line.Append(polyline);
		if (orderElements) orderedElemList.AddToFront(NumToStr(newLine));
		return;
	}

	# Check to see if we need to reverse the polyline
	local numeric reverse = needToReverseLine(line, polyline, 0, line.GetNumPoints()-1);
	if (reverse==1)
	{
		polyline.Reverse();
		reversedList.SetString("1", indexOf(elemList ,newLine));
	}

	# now simply append the line
	if (doAppend(line, polyline)==1)
	{
		# append to end
		line.Append(polyline);
		if (orderElements) orderedElemList.AddToEnd(NumToStr(newLine));
	}
	else if (doAppend(line, polyline)==2)
	{
		# append to front
		polyline.Append(line);
		line.Clear();
		line.Append(polyline);
		if (orderElements) orderedElemList.AddToFront(NumToStr(newLine));
	}
	else 
	{
		# can occur if loop is made, should clear selection
		PopupMessage("Cannot append disjoint line");
	}
}

# set the global graph offsets
proc setGraphOffsets(class GC gc, numeric fontHeight, numeric axisLabelOffset)
{
	# set the min and max display variables
	local string min$ = sprintf("%0.2f", graphMinZ);
	local string max$ = sprintf("%0.2f", graphMaxZ);
	if (graphMaxZ==-MAX_NUMBER) max$ = "null";
	if (graphMinZ==MAX_NUMBER) min$ = "null";
	# PopupMessage( sprintf("font height = %d", NumToStr(fontHeight) ) );
	if (graphMaxZ == graphMinZ) max$ = "";
	local numeric size = max(gc.TextGetWidth(max$), gc.TextGetWidth(min$));
	# PopupMessage( sprintf("max$ width = %d, min$ width = %d, size = %d", gc.TextGetWidth(max$), gc.TextGetWidth(min$), size) );

	size = max(size, fontHeight+axisLabelOffset);
	leftGraphOffset = size - 4;
	# PopupMessage( sprintf("size = %d, height+offset = %d, leftGraphOffset = %d", size, fontHeight+axisLabelOffset, leftGraphOffset) );
}

# Procedure used to draw the graph; passed a special GC
# created by the following procedure.
proc drawGraph()
{
	# save pipe top and bottom for highlighting
	pipeBottomSave = pipeBottom;
	pipeTopSave = pipeTop;
	pipeFaceSave = pipeFace;

	# create graphics context for graph
	createGC();
	gc.DrawTextSetFont("ARIAL");
	local class COLOR color;

	# set up the affine transformation for the graph
	setTrans(pipeBottom);

	# set up graph axes
	local string xlabel = "Distance (m)";
	local string ylabel = "Elevation (m)";
	local numeric drawTwoPointLines = 0;
	local numeric drawStartEndPoints = 1;

	# set the graph offsets - (globals)
	local numeric fontHeight = 12, axisLabelOffset = 3;
	setGraphOffsets(gc, fontHeight, axisLabelOffset);

	# fill in the background
	local class COLOR bgcolor;
	bgcolor.red = 98; bgcolor.green = 98; bgcolor.blue = 98;
	drawBackground(gc, bgcolor);

	# draw the grid
	color.red = 80; color.green = 80; color.blue = 80;
	drawGrid(gc, getGridIntervalX(), getGridIntervalY(), pipeBottom, color);

	# Draw and label the axes
	color.red = 0; color.green = 0; color.blue = 0;
	drawGraphAxes(gc, pipeBottom.GetVertex(pipeBottom.GetNumPoints()-1).x, xlabel, ylabel, drawTwoPointLines, color, fontHeight, axisLabelOffset);

	# draw the pipe bottom
	color = vectorLayer.SelectedElemColor;
#	drawPolyline(gc, pipeBottom, color);

	# draw the pipe top
#	drawPolyline(gc, pipeTop, color);

	# draw the pipe face
	local class COLOR fill = vectorLayer.SelectedElemColor;
#	fill.red = 40; fill.green = 40; fill.blue = 80;
	drawRectangles(gc, pipeFace, color, fill, fillToggle.GetValue());

	# draw the surface line (as DEM or from DB)
	class POLYLINE manholeSurfaceLine;
	if (doUseDEM())
	{
		# draw smoothed DEM surface line
		gc.DrawSetLineStyle("");
		color.red = 0; color.green = 0; color.blue = 0;
		drawPolyline(gc, smoothedSurface, color);
		manholeSurfaceLine = demSurface;
	}
	else
	{
		# draw surface line
		color.red = 0; color.green = 0; color.blue = 0;
		drawPolyline(gc, surface, color);
		manholeSurfaceLine = surface;
	}


	# draw the manholes
	color.red = 20; color.green = 80; color.blue = 20;
	drawManholes(gc, manholeDepth, manholeSurfaceLine, color);

	# draw the manhole labels
	color.red = 0; color.green = 0; color.blue = 0;
	drawManholeNames(gc, manholeSurfaceLine, manholeNames, color, "ARIAL", 12);

	canvas.Refresh(1);	# refresh the drawing canvas
}

# Procedure for drawing graph in CAD object; passed a local GC 
# when called by the OnSaveGraph() procedure
proc drawGraphForCAD(class GC gc)
{
	local class COLOR color;
	gc.DrawTextSetFont("ARIAL");

	# set up the affine transformation for the graph
	setTrans(pipeBottom);

	# set up graph axes
	local string xlabel = "Distance (m)";
	local string ylabel = "Elevation (m)";
	local numeric drawTwoPointLines = 0;
	local numeric drawStartEndPoints = 1;

	# set the graph offsets - (globals)
	local numeric fontHeight = 12, axisLabelOffset=3;
	setGraphOffsets(gc, fontHeight, axisLabelOffset);

	# fill in the background
	local class COLOR bgcolor;
	bgcolor.red = 98; bgcolor.green = 98; bgcolor.blue = 98;
	drawBackground(gc, bgcolor);

	# draw the grid
	color.red = 80; color.green = 80; color.blue = 80;
	drawGrid(gc, getGridIntervalX(), getGridIntervalY(), pipeBottom, color);

	# Draw and label the axes
	color.red = 0; color.green = 0; color.blue = 0;
	drawGraphAxes(gc, pipeBottom.GetVertex(pipeBottom.GetNumPoints()-1).x, xlabel, ylabel, drawTwoPointLines, color, fontHeight, axisLabelOffset);

	# draw the pipe bottom
	color = vectorLayer.SelectedElemColor;
#	drawPolyline(gc, pipeBottom, color);

	# draw the pipe top
#	drawPolyline(gc, pipeTop, color);

	# draw the pipe face
	local class COLOR fill = vectorLayer.SelectedElemColor;
#	fill.red = 40; fill.green = 40; fill.blue = 80;
	drawRectangles(gc, pipeFace, color, fill, fillToggle.GetValue());

	# draw the surface line (as DEM or from DB)
	class POLYLINE manholeSurfaceLine;
	if (doUseDEM())
	{
		# draw smoothed DEM surface line
		gc.DrawSetLineStyle("");
		color.red = 0; color.green = 0; color.blue = 0;
		drawPolyline(gc, smoothedSurface, color);
		manholeSurfaceLine = demSurface;
	}
	else
	{
		# draw surface line
		color.red = 0; color.green = 0; color.blue = 0;
		drawPolyline(gc, surface, color);
		manholeSurfaceLine = surface;
	}

	# draw the manholes
	color.red = 20; color.green = 80; color.blue = 20;
	drawManholes(gc, manholeDepth, manholeSurfaceLine, color);

	# draw the manhole labels
	color.red = 0; color.green = 0; color.blue = 0;
	drawManholeNames(gc, manholeSurfaceLine, manholeNames, color, "ARIAL", 12);
}


# Procedure called when Save Graph as CAD button on dialog is pressed
proc OnSaveGraph() 
{
	GetOutputCAD(CADgraph);	# prompt user to select output CAD object

	# create GREDEVICE for drawing into CAD object
	deviceCAD.Create(CADgraph , getHeight(), getWidth() );

	# create graphics context for the device
	local class GC gcCAD;
	gcCAD = deviceCAD.CreateGC();

	# call procedure to draw the graph for the CAD object
	drawGraphForCAD(gcCAD); 

	deviceCAD.Close();	# close CAD graphics device

	CloseCAD(CADgraph);	# close CAD object
}


# Called when the min or max z value is changed
proc OnChangeGraphZ()
{
	graphMinZ = graphMinSetting.GetValue();
	graphMaxZ = graphMaxSetting.GetValue();
	needsRedraw = 1;
}

# Callback for a right mouse button press
proc OnRightButtonPress()
{
	# make sure z settings are current
	OnChangeGraphZ();

	# clear the line
	demLine.Clear();
	orderedElemList.Clear();

	# Get the first line as a polyline
	class POLYLINE finalLine;

	# Get all of the lines, appending appropriately
	local numeric i;
	for (i=0; i 0 if one of the two nodes matches an end node.
# case 1: node1==endNodes[1] return 1
# case 2: node1==endNodes[2] return 2
# case 3: node2==endNodes[1] return 3
# case 4: node2==endNodes[2] return 4
func matchesEndNodes(numeric node1, numeric node2)
{
	if (node1==endNodes[1])	# S1 == S2
	{
		endNodes[1] = node2;
		return 1;
	}
	if (node1==endNodes[2])	# E1 == S2
	{
		endNodes[2] = node2;
		return 2;
	}
	if (node2==endNodes[1])	# S1 == E2
	{
		endNodes[1] = node1;
		return 3;
	}
	if (node2==endNodes[2])	# E1 == E2
	{
		endNodes[2] = node1;
		return 4;
	}
	return 0;
}

# if list has 0 -> 1 else -> 0
proc reverseBits(class STRINGLIST s)
{
	local numeric i;
	for (i=0; i=0)
	{
		local numeric doRemove = 0;
		# check to see if the current line is removable
		if (matchNum>0) doRemove = 1;
		if (doRemove)
		{
			# remove the line
			vectorLayer.Line.HighlightSingle(elemNum, "Subtract");
			elemList.Remove(index);
			reversedList.Remove(index);
			if (reversedList.GetString(0)=="1") reverseBits(reversedList);

			if(vectorLayer.Line.GetSelectedElement()==0)
			{
				endNodes[1] = -1;
				endNodes[2] = -1;
			}
		}
	}
	else
	{
		# if no lines exist, then simply append
		if (endNodes[1]==-1 && endNodes[2]==-1)
		{
			endNodes[1] = node1;
			endNodes[2] = node2;
			vectorLayer.Line.HighlightSingle(elemNum, "Add");
			elemList.AddToEnd(NumToStr(elemNum));
			reversedList.AddToEnd("0");
		}
		else if (matchNum>0) # check to see if the current line is appendable
		{
			# add the line
			vectorLayer.Line.HighlightSingle(elemNum, "Add");
			elemList.AddToEnd(NumToStr(elemNum));
			reversedList.AddToEnd("0");
		}
	}
}


# Toggle the line element closest to the point 'position'
func toggleClosestLine(class POINT2D position)
{
	local numeric elemNum = FindClosestLine(lineVector, position.x, position.y);
	if (elemNum > 0 )
	{
		toggleClosestLineElement(elemNum);
	}
	return elemNum;
}

# Callback for a left mouse button press
proc OnLeftButtonPress()
{
	# Find cursor position in screen coordinates
	local class POINT2D point;
	point.x = PointerX;
	point.y = PointerY;
	point = TransPoint2D(point, ViewGetTransLayerToScreen(View, vectorLayer, 1));

	# toggle the element as selected (if valid) or deselected (if possible)
	toggleClosestLine(point);
}

# Called when the close button is pressed.  Closes the dialogs.
proc cbClose()
{
	dlgwin.Close(0);
	if (setDefaultWhenClose)
	{
		setDefaultWhenClose = false;
		View.SetDefaultTool();
	}
}

# Called when the canvas is resized - gc is recreated before drawing
proc OnCanvasResize(class GUI_CANVAS canvas, numeric width, numeric height)
{
	OnRightButtonPress();
}

# return the element number of the nearest line to the graph point
func getNearestLineElementFromGraph(class POINT2D graphPoint)
{
	# make sure z settings are current
#	graphMinZ = graphMinSetting.GetValue();
#	graphMaxZ = graphMaxSetting.GetValue();

	local class POLYLINE splits;
	local class POINT2D tmp;
	local numeric distance = 0;
	tmp.x = distance;
	tmp.y = 0;
	splits.AppendVertex(tmp);

	local numeric i=0;
	for (i=1; i tmp.x)
	{
		vertexNum++;
	}
	if (vertexNum >= splits.GetNumPoints() || (vertexNum==1 && graphPoint.x < leftGraphOffset))
	{
		return 0;
	}

	# get the line from the vertex
	local numeric lineNum = floor(vertexNum/2);
	local numeric elemNum = getElement(orderedElemList, lineNum);

#	vertexDisplay.SetValue(vertexNum, 0);
#	lineDisplay.SetValue(lineNum, 0);
#	elemDisplay.SetValue(elemNum, 0);
	return elemNum;
}

proc highlightGraphSegment(numeric lineNum, class COLOR color)
{
	local class POLYLINE myrect;
	pipeFaceSave.Extract(lineNum*5, 5, myrect);
	drawRectangles(gc, myrect, color, color, fillToggle.GetValue());

	# draw the manholes
	color.red = 20; color.green = 80; color.blue = 20;
	drawManholes(gc, pipeBottomSave, pipeTopSave, color);

	canvas.Refresh(1);
}

proc makeLineActive(numeric elemNum)
{
	vectorLayer.Line.SetActiveElement(elemNum);

	local numeric lineOrdering = indexOf(orderedElemList, elemNum);
	if (lineOrdering>-1 && currentlyActive != lineOrdering)
	{
		local class COLOR color = vectorLayer.SelectedElemColor;
		if(currentlyActive>-1) highlightGraphSegment(currentlyActive, color);
		currentlyActive = lineOrdering;
#color.red = 90; color.green = 90; color.blue = 100;
#highlightGraphSegment(currentlyActive, color);
		color = vectorLayer.ActiveElemColor;
		highlightGraphSegment(currentlyActive, color);

		materialDisplay.SetValue(readLineTableRecordStr(elemNum, "CAN_MAT"), 0);
		slopeDisplay.SetValue(readLineTableRecord(elemNum, "CAN_SLOPE"), 0);
		diameterDisplay.SetValue(readLineTableRecord(elemNum, "CAN_DIAM")/1000, 0);
	}
}


func getNearestLineElementFromGraph2(class POINT2D graphPoint)
{

	# make sure z settings are current
#	graphMinZ = graphMinSetting.GetValue();
#	graphMaxZ = graphMaxSetting.GetValue();

	local class POLYLINE splits = pipeBottomSave;
	local class POINT2D tmp;
#	local numeric distance = 0;
#	tmp.x = distance;
#	tmp.y = 0;
#	splits.AppendVertex(tmp);
#
#	local numeric i=0;
#	for (i=1; i tmp.x)
	{
		vertexNum++;
	}
	if (vertexNum >= splits.GetNumPoints() || (vertexNum==1 && graphPoint.x < leftGraphOffset))
	{
		return 0;
	}

	# get the line from the vertex
	local numeric lineNum = floor(vertexNum/2);
	local numeric elemNum = getElement(orderedElemList, lineNum);

#	vertexDisplay.SetValue(vertexNum, 0);
#	lineDisplay.SetValue(lineNum, 0);
#	elemDisplay.SetValue(elemNum, 0);
	return elemNum;
}

# Called when the mouse is moved over the canvas - highlights the nearest line in the 2d view
proc OnCanvasMouseMove(class GUI_CANVAS canvas, class POINT2D point, numeric shift, numeric ctrl)
{

#
local class POINT2D mapPoint = trans.ConvertPoint2DInv(point);
if (!IsNull(mapPoint.x) && !IsNull(mapPoint.y))
{
	mouseXDisplay.SetValue(mapPoint.x,0);
	mouseYDisplay.SetValue(mapPoint.y,0);
}
#


	if (needsRedraw)
	{
		OnRightButtonPress();
	}
	local numeric elemNum = getNearestLineElementFromGraph(point);
	if (elemNum > 0)
	{
		makeLineActive(elemNum);
	}
	needsRedraw = 0;
}


# Called when mouse is moved over the 2D view - highlights the nearest line in the 2d view
proc OnPointerMoveNoButton()
{
	if (needsRedraw)
	{
		OnRightButtonPress();
		needsRedraw = 0;
	}
	local class POINT2D pointer;
	pointer.x = PointerX;
	pointer.y = PointerY;

	# Get the cursor position in map coords
	local class TRANSPARM screenToView = ViewGetTransViewToScreen(View, 1);
	local class TRANSPARM viewToLayer = ViewGetTransLayerToView(View, vectorLayer, 1);
	pointer = TransPoint2D(pointer, screenToView);
	pointer = TransPoint2D(pointer, viewToLayer);

	local numeric elemNum = FindClosestLine(lineVector, pointer.x, pointer.y);
	if (elemNum > 0)
	{
		makeLineActive(elemNum);
	}
}

# Called when the button to clear the lines is pressed
proc OnClearLines()
{
	endNodes[1]=-1;
	endNodes[2]=-1;
	elemList.Clear();
	orderedElemList.Clear();
	reversedList.Clear();
	vectorLayer.UnhighlightAllElements(1);
	materialDisplay.SetValue("", 0);
	slopeDisplay.SetValue(0, 0);
	diameterDisplay.SetValue(0, 0);
	mouseXDisplay.SetValue(0, 0);
	mouseYDisplay.SetValue(0, 0);
	OnRightButtonPress();
}

# Called with the grid toggle button is pressed, does a full redraw
proc OnGridTogglePressed()
{
	OnRightButtonPress();
}

# Called with the fill toggle button is pressed, does a full redraw
proc OnFillTogglePressed()
{
	OnRightButtonPress();
}

# Called with the grid toggle button is pressed, checks that dem exists
proc OnDemTogglePressed()
{
	checkLayer();
}

# Called the first time the tool is activated.
# If the tool implements a dialog it should be created (but not displayed) here.
func OnInitialize()
{
	# initialize the end node values, used to validate line appends
	endNodes[1]=-1;
	endNodes[2]=-1;

	# handle as layout or as group
	if (Layout)
	{
		WidgetAddCallback(Layout.GroupSelectedCallback, cbGroup);
		activegroup = Layout.ActiveGroup;
	}
	else activegroup = Group;
	WidgetAddCallback(activegroup.LayerSelectedCallback, cbLayer);

	# define the dialog here with xml specification
	string xml$ = 
	'
	
	
		
			
				
					
					
						
						
					
					
						
						
					
				
			
			
				
					
					
						
						
					
					
						
						
					
				
			
			
				
			
			
				
					
						
						
					
					
						
						
						
					
					
						
						
					
				
			
			
				
					
						
							
							
						
						
							
							
						
					
				
				
					
						
					
				
			


			
				
				
			
		
	
	';

	# Parse the xml dialog specification
	local class XMLDOC dlgdoc;
	local numeric err = dlgdoc.Parse(xml$);
	if (err < 0) {
		PopupError(err);
		Exit();
		}

	local string dlgid$ = "guicanvas";
	local class XMLNODE dlgnode = dlgdoc.GetElementByID(dlgid$);
	if (dlgnode == 0) {
		PopupMessage("Could not find specified id: "+dlgid$);
		Exit();
		}
	dlgwin.SetXMLNode(dlgnode);

	# create the dialog as a modeless dialog
	err = dlgwin.CreateModeless();
	if (err < 0) {
		PopupError(err);
		Exit();
		}

	# get the control for the drawing canvas
	canvas = dlgwin.GetCtrlByID("canvas");
	canvas.SetOnSize(OnCanvasResize);
	canvas.SetOnRightDown(OnCanvasResize);
	canvas.SetOnMouseMove(OnCanvasMouseMove);

	# get the control for the grid toggle
	gridToggle = dlgwin.GetCtrlByID("gridtoggle");
	gridToggle.SetValue(1,0);
	gridToggle.SetOnPressed(OnGridTogglePressed);

	# get the control for the dem toggle
	demToggle = dlgwin.GetCtrlByID("demtoggle");
	demToggle.SetOnPressed(OnDemTogglePressed);

	# get the controls for the grid interval settings
	gridIntervalX = dlgwin.GetCtrlByID("xinterval");
	gridIntervalY = dlgwin.GetCtrlByID("yinterval");

	# get the controls for the min and max z setting
	graphMinSetting = dlgwin.GetCtrlByID("minz");
	graphMaxSetting = dlgwin.GetCtrlByID("maxz");
	OnChangeGraphZ();

	# get the control for the grid toggle
	fillToggle = dlgwin.GetCtrlByID("filltoggle");
	fillToggle.SetValue(1,0);
	fillToggle.SetOnPressed(OnFillTogglePressed);

	# get control for display of info
#	vertexDisplay = dlgwin.GetCtrlByID("vertex");
#	lineDisplay = dlgwin.GetCtrlByID("line");
#	elemDisplay = dlgwin.GetCtrlByID("elem");
	mouseXDisplay = dlgwin.GetCtrlByID("mousex");
	mouseYDisplay = dlgwin.GetCtrlByID("mousey");
	materialDisplay = dlgwin.GetCtrlByID("material");
	slopeDisplay = dlgwin.GetCtrlByID("slope");
	diameterDisplay = dlgwin.GetCtrlByID("diameter");

	drawable = 0;
}
 
# Called when tool is to be destroyed, will not be called if tool was never activated.
func OnDestroy()
{
	dlgwin.Close(0);
}

# Called when tool is activated.
func OnActivate()
{
	checkLayer();

	# open the graph dialog window
	dlgwin.Open();

	# draw the graph
	OnRightButtonPress();

	setDefaultWhenClose = true;
}
 
# Called when tool is deactivated (usually when switching to another tool).
func OnDeactivate()
{
	setDefaultWhenClose = false;
	cbClose();
}


Back Home ©MicroImages, Inc. 2008 Published in the United States of America
11th Floor - Sharp Tower, 206 South 13th Street, Lincoln NE 68508-2010   USA
Business & Sales: (402)477-9554  Support: (402)477-9562  Fax: (402)477-9559
Business info@microimages.com  Support support@microimages.com  Web webmaster@microimages.com

9 May 2008

page update: 14 Aug 07