' Copyright 2008 ESRI
'
' All rights reserved under the copyright laws of the United States
' and applicable international laws, treaties, and conventions.
'
' You may freely redistribute and use this sample code, with or
' without modification, provided you include the original copyright
' notice and use restrictions.
'
' See use restrictions at <your ArcGIS install location>/developerkit/userestrictions.txt.
'
'The Individual Flow Direction tool allows the user to look up and set the
'flow direction for an individual edge element in the current geometric network.
'In order to be able to set the flow direction, an edit session is required.
Option Explicit
Implements ICommand
Implements ITool
'declare a module-level variable to hold a reference to the ArcMap application
Private m_pApplication As esriFramework.IApplication
'declare a module-level variable to hold a reference to the Utility Network
'Analysis extension
Private m_pUtilityNetworkAnalysisExt As esriEditorExt.IUtilityNetworkAnalysisExt
'declare a module-level variable to hold a reference to the Editor extension
Private m_pEditor As esriEditor.IEditor
'declare a module-level variable to hold a reference to the current geometric network
Private m_pGeometricNetwork As esriGeoDatabase.IGeometricNetwork
'declare a module-level variable to hold the EID that was clicked
Private m_lEID As Long
'declare a module-level variable to hold the geometry of the EID that was clicked
Private m_pGeometry As esriGeometry.IGeometry
'declare a module-level variable to hold the line symbol for drawing
Private m_pLineSymbol As esriDisplay.ILineSymbol
'declare module-level variables to hold references to pictures
Private m_pBitmap As IPictureDisp
Private m_pCursor As IPictureDisp
'declare a module-level variable to handle events from the active view
Private WithEvents m_pActiveViewEvents As esriCarto.Map
'declare module-level variables to handle events from the form
Private WithEvents frmFlowDirToolDialogEvents As Form
Private WithEvents cmdOKButtonEvents As CommandButton
Private Sub Class_Initialize()
Dim pRgbColor As IRgbColor
Dim pLineSymbol As ISimpleLineSymbol
'make a blue color object
Set pRgbColor = New RgbColor
With pRgbColor
.Red = 0
.Green = 0
.Blue = 255
End With
'create a new simple line symbol that is blue and has width 4
Set pLineSymbol = New SimpleLineSymbol
pLineSymbol.Color = pRgbColor
pLineSymbol.Width = 4
'set it to the module-level variable
Set m_pLineSymbol = pLineSymbol
'load the bitmap and cursor images into the module variables
Set m_pBitmap = LoadResPicture(101, 0)
Set m_pCursor = LoadResPicture(102, 2)
End Sub
Private Sub Class_Terminate()
'release the form and active view event variables upon termination of this command
Set frmFlowDirToolDialogEvents = Nothing
Set cmdOKButtonEvents = Nothing
Set m_pActiveViewEvents = Nothing
End Sub
Private Property Get ICommand_Bitmap() As esriSystem.OLE_HANDLE
ICommand_Bitmap = m_pBitmap
End Property
Private Property Get ICommand_Caption() As String
ICommand_Caption = "Individual Flow Direction Tool"
End Property
Private Property Get ICommand_Category() As String
ICommand_Category = "Developer Samples"
End Property
Private Property Get ICommand_Checked() As Boolean
ICommand_Checked = False
End Property
Private Property Get ICommand_Enabled() As Boolean
'Enable the tool if we are in Data View and the network extension has a network
Dim pMxDoc As IMxDocument
Dim pViewManager As IViewManager
Dim pNetworkAnalysisExt As INetworkAnalysisExt
ICommand_Enabled = False
'if the current active view uses page coordinates, then we are in Layout View
'do not enable the tool
Set pMxDoc = m_pApplication.Document
Set pViewManager = pMxDoc.ActiveView
If pViewManager.UsesPageCoordinates Then Exit Sub
'if the focus map has at least one network, then enable the tool
Set pNetworkAnalysisExt = m_pUtilityNetworkAnalysisExt
If pNetworkAnalysisExt.NetworkCount > 0 Then
ICommand_Enabled = True
End If
End Property
Private Property Get ICommand_HelpContextID() As Long
End Property
Private Property Get ICommand_HelpFile() As String
End Property
Private Property Get ICommand_Message() As String
ICommand_Message = "Get and set the flow direction of an individual edge element."
End Property
Private Property Get ICommand_Name() As String
ICommand_Name = "IFDTool.clsFlowDirTool"
End Property
Private Sub ICommand_OnClick()
End Sub
Private Sub ICommand_OnCreate(ByVal hook As Object)
Dim pUID_UNA As New esriSystem.UID
Dim pUID_Editor As New esriSystem.UID
'set a reference to the application
Set m_pApplication = hook
'obtain a reference to the Utility Network Analysis extension
pUID_UNA = "esriEditorExt.UtilityNetworkAnalysisExt"
Set m_pUtilityNetworkAnalysisExt = m_pApplication.FindExtensionByCLSID(pUID_UNA)
'obtain a reference to the Editor extension
pUID_Editor = "esriEditor.Editor"
Set m_pEditor = m_pApplication.FindExtensionByCLSID(pUID_Editor)
End Sub
Private Property Get ICommand_Tooltip() As String
ICommand_Tooltip = "Get and set the flow direction of an individual edge element."
End Property
Private Property Get ITool_Cursor() As esriSystem.OLE_HANDLE
ITool_Cursor = m_pCursor
End Property
Private Function ITool_Deactivate() As Boolean
ITool_Deactivate = True
End Function
Private Function ITool_OnContextMenu(ByVal X As Long, ByVal Y As Long) As Boolean
End Function
Private Sub ITool_OnDblClick()
End Sub
Private Sub ITool_OnKeyDown(ByVal KeyCode As Long, ByVal Shift As Long)
End Sub
Private Sub ITool_OnKeyUp(ByVal KeyCode As Long, ByVal Shift As Long)
End Sub
Private Sub ITool_OnMouseDown(ByVal Button As Long, ByVal Shift As Long, ByVal X As Long, ByVal Y As Long)
Dim pMxDoc As IMxDocument
Dim pMap As IMap
Dim pNetworkAnalysisExt As INetworkAnalysisExt
Dim lSnapTolerance As Long
Dim dSearchRadius As Double
Dim pPoint As IPoint
Dim pPointToEID As IPointToEID
Dim pLoc As IPoint
Dim dPercent As Double
Dim pNetElements As INetElements
Dim pEnumNetEIDBuilder As IEnumNetEIDBuilder
Dim pEnumNetEID As IEnumNetEID
Dim pActiveView As IActiveView
Dim pEIDHelper As IEIDHelper
Dim pEnumEIDInfo As IEnumEIDInfo
Dim lEIDInfoCount As Long
Dim pEIDInfo As IEIDInfo
On Error GoTo ErrorHandler
'initialize the form event variables
Set frmFlowDirToolDialogEvents = frmFlowDirToolDialog
Set cmdOKButtonEvents = frmFlowDirToolDialog.OKButton
'initialize the active view event variable
Set pMxDoc = m_pApplication.Document
Set pMap = pMxDoc.FocusMap
Set m_pActiveViewEvents = pMap
'get parameters from the Utility Network Analysis Extension
Set pNetworkAnalysisExt = m_pUtilityNetworkAnalysisExt
Set m_pGeometricNetwork = pNetworkAnalysisExt.CurrentNetwork
lSnapTolerance = pNetworkAnalysisExt.SnapTolerance
'if there is no geometric network, display an error
If m_pGeometricNetwork Is Nothing Then
MsgBox "There is no current geometric network."
Exit Sub
End If
'convert the X,Y coordinate to a point in the map
Set pPoint = pMxDoc.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(X, Y)
'convert the snap tolerance (in pixels) to a search radius in map units
Set pActiveView = pMxDoc.ActiveView
dSearchRadius = Pixels2MapUnits(lSnapTolerance, pActiveView)
'find the nearest edge element to the point
Set pPointToEID = New PointToEID
Set pPointToEID.GeometricNetwork = m_pGeometricNetwork
Set pPointToEID.SourceMap = pMap
pPointToEID.SnapTolerance = dSearchRadius
pPointToEID.GetNearestEdge pPoint, m_lEID, pLoc, dPercent
Set pNetElements = m_pGeometricNetwork.Network
If Not pNetElements.IsValidElement(m_lEID, esriETEdge) Then
'beep and display an error message in ArcMap's status bar
Beep
m_pApplication.StatusBar.Message(esriStatusMain) = _
"No edge feature found at this location"
Exit Sub
End If
'create a new EnumNetEIDArray object and add m_lEID to it
Set pEnumNetEIDBuilder = New EnumNetEIDArray
Set pEnumNetEIDBuilder.Network = m_pGeometricNetwork.Network
pEnumNetEIDBuilder.ElementType = esriETEdge
pEnumNetEIDBuilder.Add m_lEID
Set pEnumNetEID = pEnumNetEIDBuilder
'create a new EIDHelper object and set its parameters to return only the geometries
Set pEIDHelper = New EIDHelper
Set pEIDHelper.GeometricNetwork = m_pGeometricNetwork
Set pEIDHelper.DisplayEnvelope = pActiveView.Extent
Set pEIDHelper.OutputSpatialReference = pActiveView.FocusMap.SpatialReference
pEIDHelper.PartialComplexEdgeGeometry = True
pEIDHelper.ReturnGeometries = True
pEIDHelper.ReturnFeatures = False
'create the EIDInfo enumeration with the geometries and get its count
Set pEnumEIDInfo = pEIDHelper.CreateEnumEIDInfo(pEnumNetEID)
lEIDInfoCount = pEnumEIDInfo.Count
'if there is more than 1 EIDInfo object, then there is an error!
If lEIDInfoCount <> 1 Then
MsgBox "Error: " & lEIDInfoCount & " geometries were returned by the EIDHelper!"
Exit Sub
End If
'get the geometry and set it to the module variable
pEnumEIDInfo.Reset
Set pEIDInfo = pEnumEIDInfo.Next
If pEIDInfo Is Nothing Then
MsgBox "Error: EIDInfo Enumeration is empty!"
Exit Sub
End If
If pEIDInfo.Geometry Is Nothing Then
MsgBox "Error: The geometry is empty!"
Exit Sub
End If
Set m_pGeometry = pEIDInfo.Geometry
'call partial refresh on the graphics to draw the geometry to the screen
MyPartialRefresh pActiveView, m_pGeometry
'show the form
frmFlowDirToolDialog.Show vbModal
Exit Sub
ErrorHandler:
MsgBox "Error # " & Str(Err.Number) & " was generated by " _
& Err.Source & Chr(13) & Err.Description
End Sub
Private Sub ITool_OnMouseMove(ByVal Button As Long, ByVal Shift As Long, ByVal X As Long, ByVal Y As Long)
End Sub
Private Sub ITool_OnMouseUp(ByVal Button As Long, ByVal Shift As Long, ByVal X As Long, ByVal Y As Long)
End Sub
Private Sub ITool_Refresh(ByVal hDc As esriSystem.OLE_HANDLE)
End Sub
Private Sub frmFlowDirToolDialogEvents_Load()
Dim pNetElements As INetElements
Dim lFCID As Long, lFID As Long, lSubID As Long
Dim pFCContainer As IFeatureClassContainer
Dim pFeatureClass As IFeatureClass
Dim pDataset As IDataset
Dim sFCName As String
Dim pUtilityNetwork As IUtilityNetwork
'get the Feature Class ID, Feature ID, and Sub ID for m_lEID
Set pNetElements = m_pGeometricNetwork.Network
pNetElements.QueryIDs m_lEID, esriETEdge, lFCID, lFID, lSubID
'get the name of the feature class for this FCID
Set pFCContainer = m_pGeometricNetwork
Set pFeatureClass = pFCContainer.ClassByID(lFCID)
Set pDataset = pFeatureClass
sFCName = pDataset.Name
With frmFlowDirToolDialog
'populate the text labels on the form
.lblFCNameData = sFCName
.lblFCIDData = CStr(lFCID)
.lblFIDData = CStr(lFID)
.lblSubIDData = CStr(lSubID)
.lblEIDData = CStr(m_lEID)
'look up the current flow for m_lEID and click the appropriate option button
Set pUtilityNetwork = m_pGeometricNetwork.Network
.ClickFlowDirButton pUtilityNetwork.GetFlowDirection(m_lEID)
'if we are not in an edit session for this map then do not enable radio buttons
If m_pEditor.EditState <> esriStateEditing Then
.SetFlowButtonsEnabled False
End If
'disable the OK button, since no edits have been made yet
.OKButton.Enabled = False
End With
End Sub
Private Sub frmFlowDirToolDialogEvents_Unload(Cancel As Integer)
Dim pGeometry As IGeometry
Dim pMxDoc As IMxDocument
'make a copy of the EID's geometry before clearing the module-level variable
Set pGeometry = m_pGeometry
Set m_pGeometry = Nothing
'call partial refresh on the graphics to clear the geometry from the screen
Set pMxDoc = m_pApplication.Document
MyPartialRefresh pMxDoc.ActiveView, pGeometry
End Sub
Private Sub cmdOKButtonEvents_Click()
'set the flow direction to the network
SetFlowDirection
'close the form
Unload frmFlowDirToolDialog
End Sub
'when the after draw event is fired for the esriViewGraphics phase,
'draw the edge element's geometry (m_pGeometry) using the blue line symbol (m_pLineSymbol)
Private Sub m_pActiveViewEvents_AfterDraw(ByVal Display As IDisplay, ByVal phase As esriViewDrawPhase)
If phase = esriViewGraphics Then
Display.StartDrawing 0, esriNoScreenCache
Display.SetSymbol m_pLineSymbol
If Not m_pGeometry Is Nothing Then
Display.DrawPolyline m_pGeometry
End If
Display.FinishDrawing
End If
End Sub
'performs a partial refresh that expands m_pGeometry's envelope to take into account
'the line symbol width and (if displayed) the flow direction symbol size
Private Sub MyPartialRefresh(pActiveView As IActiveView, pGeometry As IGeometry)
Dim pEnvelope As IEnvelope
Dim dLineExp As Double
Dim dXExp As Double, dYExp As Double
Dim pUNAExtFlow As IUtilityNetworkAnalysisExtFlow
Dim dCurrentSymbolSize As Double, dMaxSymbolSize As Double
Dim dMaxSymbolSizeInMapUnits As Double
'get a copy of the geometry's envelope
Set pEnvelope = pGeometry.Envelope
'find out how much we need to expand the envelope
'find the expansion needed to take into account the width of the line symbol
dLineExp = pActiveView.ScreenDisplay.DisplayTransformation.FromPoints(m_pLineSymbol.Width)
dXExp = dLineExp
dYExp = dLineExp
'get a reference to the Utility Network Analysis Extension and QI for the
'IUtilityNetworkAnalysisExtFlow interface
Set pUNAExtFlow = m_pUtilityNetworkAnalysisExt
'if flow direction indicators are being drawn in the map, then
If pUNAExtFlow.ShowFlow Then
'find the largest symbol size used for indicating flow direction
dMaxSymbolSize = pUNAExtFlow.ArrowSymbol.Size
dCurrentSymbolSize = pUNAExtFlow.IndeterminateSymbol.Size
If dCurrentSymbolSize > dMaxSymbolSize Then dMaxSymbolSize = dCurrentSymbolSize
dCurrentSymbolSize = pUNAExtFlow.UninitializedSymbol.Size
If dCurrentSymbolSize > dMaxSymbolSize Then dMaxSymbolSize = dCurrentSymbolSize
'find out the size of the largest symbol in map units for the current display
dMaxSymbolSizeInMapUnits = pActiveView.ScreenDisplay.DisplayTransformation.FromPoints(dMaxSymbolSize)
'if the envelope size after taking into account the width of line symbol is still
'too small to enclose the flow direction symbol, adjust the expansion values
If (pEnvelope.Width + dLineExp) < dMaxSymbolSizeInMapUnits Then
dXExp = dMaxSymbolSizeInMapUnits - pEnvelope.Width
End If
If (pEnvelope.Height + dLineExp) < dMaxSymbolSizeInMapUnits Then
dYExp = dMaxSymbolSizeInMapUnits - pEnvelope.Height
End If
End If
'expand the envelope
pEnvelope.Expand dXExp, dYExp, False
'refresh the graphics layer to redraw the edge element
pActiveView.PartialRefresh esriViewGraphics, Nothing, pEnvelope
End Sub
'converts the specified pixel distance into a map unit distance
Private Function Pixels2MapUnits(lPixelDistance As Long, pActiveView As IActiveView) As Double
Dim pDT As IDisplayTransformation
Dim deviceRECT As tagRECT
Dim lExtentInPixels As Long
Dim pEnvelope As IEnvelope
Dim dExtentInMapUnits As Double
Dim dOnePixelInMapUnits As Double
Set pDT = pActiveView.ScreenDisplay.DisplayTransformation
'find the width of the device frame in pixels
deviceRECT = pDT.DeviceFrame
lExtentInPixels = deviceRECT.Right - deviceRECT.Left
'find the width of the device frame in map units
Set pEnvelope = pDT.VisibleBounds
dExtentInMapUnits = pEnvelope.Width
'calculate what one pixel is in map units
dOnePixelInMapUnits = dExtentInMapUnits / lExtentInPixels
'multiply by the pixel distance to find the distance in map units
Pixels2MapUnits = lPixelDistance * dOnePixelInMapUnits
End Function
'sets the flow direction for the m_lEID based on the button pressed on the form
'NOTE: this Sub does not refresh the display (refresh occurs when the form is Unloaded)
Private Sub SetFlowDirection()
Dim pUtilityNetwork As esriGeoDatabase.IUtilityNetwork
'change the mouse pointer to an hourglass during setting flow direction and redraw
frmFlowDirToolDialog.MousePointer = vbHourglass
'create an edit operation enabling an undo for this operation
m_pEditor.StartOperation
'copy the flow direction info from the option button to the network
Set pUtilityNetwork = m_pGeometricNetwork.Network
pUtilityNetwork.SetFlowDirection m_lEID, frmFlowDirToolDialog.getFlowDirButton
'stop the edit operation and specify the name of this edit operation
m_pEditor.StopOperation "Set Flow Direction"
'change the mouse pointer back to default
frmFlowDirToolDialog.MousePointer = vbDefault
End Sub