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