# 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();
}