About the Using the traversal result to select source features Sample
[C#]
SelectFeaturesTool.cs
using System; using System.Windows.Forms; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.NetworkAnalyst; using ESRI.ArcGIS.NetworkAnalystUI; namespace NATraversalResult { public class SelectFeaturesTool : ESRI.ArcGIS.Desktop.AddIns.Tool { INetworkAnalystExtension m_networkAnalystExtension; public SelectFeaturesTool() { m_networkAnalystExtension = ArcMap.Application.FindExtensionByName("Network Analyst") as INetworkAnalystExtension; } protected override void OnUpdate() { Enabled = ArcMap.Application != null; } private INALayer GetActiveAnalysisLayer() { if (m_networkAnalystExtension != null) return m_networkAnalystExtension.NAWindow.ActiveAnalysis; else return null; } protected override bool OnDeactivate() { return true; } /// <summary> /// Finds the closest feature to the point clicked that corresponds to a NATraversalResultElement in the active analysis layer's traversal result /// </summary> /// protected override void OnMouseDown(MouseEventArgs e) { try { // Get the current network analysis traversal result INALayer naLayer = GetActiveAnalysisLayer(); if (naLayer == null) throw new Exception("Null NALayer"); INATraversalResult naTraversalResult = (INATraversalResult)naLayer.Context.Result; if (naTraversalResult == null || !((INAResult)naTraversalResult).HasValidResult) throw new Exception("Active analysis layer has an invalid result."); // Get the current map IMap map = ArcMap.Document.FocusMap; IActiveView activeView = ArcMap.Document.ActiveView; // Get the current point and buffer it into a polygon based on the search tolerance IPoint point = activeView.ScreenDisplay.DisplayTransformation.ToMapPoint(e.X, e.Y); IProximityOperator proximityOperator = point as IProximityOperator; ITopologicalOperator topologicalOperator = point as ITopologicalOperator; IPolygon polygon = (IPolygon)topologicalOperator.Buffer(activeView.Extent.Width / 20); double closestDistance = activeView.Extent.Width / 20; IFeature closestFeature = null; // Setup the spatial filter ISpatialFilter spatialFilter = new SpatialFilterClass(); spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelEnvelopeIntersects; spatialFilter.Geometry = polygon.Envelope; //Loop through the layers in the map that participate in the network dataset //Find the closest feature IEnumLayer enumLayer = map.get_Layers(null, true); ILayer layer = enumLayer.Next(); while (layer != null) { IFeatureLayer featureLayer = layer as IFeatureLayer; if (featureLayer != null) { if (featureLayer.Visible) { IFeatureClass featureClass = featureLayer.FeatureClass; // loop though all the sources in the network, trying to see if it corresponds to a source in the traversal result. for (int sourceIndex = 0; sourceIndex < naTraversalResult.SourceCount; sourceIndex++) { // Check to see if the feature class matches the traversal result source's feature class ITable currentTable = featureClass as ITable; ITable sourceTable = naTraversalResult.get_Source(sourceIndex).Table; if (currentTable == sourceTable) { // Since we correspond to a source in the traversal result, find the closest feature to the point clicked. spatialFilter.GeometryField = featureClass.ShapeFieldName; IFeatureCursor featureCursor = featureClass.Search(spatialFilter, false); IFeature feature = featureCursor.NextFeature(); while (feature != null) { double distance = proximityOperator.ReturnDistance(feature.Shape); if (distance < closestDistance) { closestFeature = feature; closestDistance = distance; } feature = featureCursor.NextFeature(); } } } } } layer = enumLayer.Next(); } if (closestFeature == null) MessageBox.Show("Could not find a feature"); else SelectTraversedFeatures(closestFeature); // Since we found a feature, select the features traversed to get to it. } catch (Exception exception) { MessageBox.Show(exception.Message, "Error"); } } /// <summary> /// Selects all the features in the visible feature layers that correspond to NATraversalResultElements /// that were traversed to get to the selected feature. Do this by using INATraversalResultQuery to walk /// the traversal result. /// </summary> /// private void SelectTraversedFeatures(IFeature selectedFeature) { // Get reference to the current map IMap map = ArcMap.Document.FocusMap; // Get the NATraversalResult INALayer naLayer = GetActiveAnalysisLayer(); INAContext naContext = naLayer.Context; INATraversalResult naTraversalResult = (INATraversalResult)naContext.Result; INATraversalResultEdit naTraversalResultEdit = (INATraversalResultEdit)naTraversalResult; INATraversalResultQuery naTraversalResultQuery = (INATraversalResultQuery)naTraversalResult; // Cache the FeatureClass of each input source // Also create a LongArray to hold the OIDs of features traversed // This makes the assumption that the sources go from 1 to N INATraversalResultSource selectedSource = null; IFeatureClass[] sourceFeatureClasses = new IFeatureClass[naTraversalResult.SourceCount + 1]; // (will never use index 0) ILongArray[] sourceOIDs = new ILongArray[naTraversalResult.SourceCount + 1]; // (will never use index 0) for (int sourceIndex = sourceFeatureClasses.GetLowerBound(0); sourceIndex <= sourceFeatureClasses.GetUpperBound(0); sourceIndex++) { INATraversalResultSource naTraversalResultSource = naTraversalResult.get_SourceByID(sourceIndex); if (naTraversalResultSource != null) { IFeatureClass featureClass = naTraversalResultSource.Table as IFeatureClass; if (featureClass != null) { sourceFeatureClasses[sourceIndex] = featureClass; // If this source is the one of the selected feature, // remember it for later when we find it's traversal elements if (featureClass == selectedFeature.Class) selectedSource = naTraversalResultSource; sourceOIDs[sourceIndex] = new LongArrayClass(); } } } // Verify the selected feature's class is actually in the network if (selectedSource == null) throw (new Exception("The selected feature is not part of the network.")); // Get the traversal edges corresponding to the selected feature // There may be more than one returned if there are multiple facilities // or if the edge was traversed in both directions. IQueryFilter queryFilter = new QueryFilterClass(); queryFilter.WhereClause = "SourceID = " + selectedSource.ID + " and SourceOID = " + selectedFeature.OID; // Figure out if the selected feature is a point or line // and get the correct traversal feature class IFeatureClass traversalFClass; if (selectedFeature.Shape.GeometryType == esriGeometryType.esriGeometryPoint) traversalFClass = naTraversalResultQuery.get_FeatureClass(esriNetworkElementType.esriNETJunction); else if (selectedFeature.Shape.GeometryType == esriGeometryType.esriGeometryPolyline) traversalFClass = naTraversalResultQuery.get_FeatureClass(esriNetworkElementType.esriNETEdge); else throw (new Exception("You must start from a point or polyline")); if (traversalFClass.FeatureCount(null) == 0) throw (new Exception("The traversal result is empty. Please click solve.")); // Search through the traversal class to find those corresponding to the selected feature INATraversalResultElement naTraversalResultElement; IFeatureCursor featureCursor = traversalFClass.Search(queryFilter, true); IFeature feature = featureCursor.NextFeature(); if (feature == null) throw (new Exception("You must pick a feature that was traversed.")); // Loop through all the traversal result elements for that feature while (feature != null) { naTraversalResultElement = (INATraversalResultElement)feature; sourceOIDs[naTraversalResultElement.SourceID].Add(naTraversalResultElement.SourceOID); //Get the connected traversal elements going backwards OutputConnected(naTraversalResultQuery, naTraversalResultElement, esriRelDirection.esriRelDirectionBackward, sourceOIDs); feature = featureCursor.NextFeature(); } // Select the features in the Map SelectFeatures(map, sourceFeatureClasses, sourceOIDs); } /// <summary> /// This function selects the features corresponding to the arrays of feature classes and OIDs /// </summary> private void SelectFeatures(IMap map, IFeatureClass[] sourceFeatureClasses, ILongArray[] sourceOIDs) { IActiveView activeView = (IActiveView)map; IEnumLayer enumLayer = map.get_Layers(null, true); ILayer layer = enumLayer.Next(); while (layer != null) { IFeatureLayer featureLayer = layer as IFeatureLayer; IFeatureSelection featureSelection = layer as IFeatureSelection; if ((featureLayer != null) && (featureSelection != null) && featureLayer.Visible && featureLayer.Selectable) { for (int sourceIndex = sourceFeatureClasses.GetLowerBound(0); sourceIndex <= sourceFeatureClasses.GetUpperBound(0); sourceIndex++) { if (featureLayer.FeatureClass == sourceFeatureClasses[sourceIndex]) { //Clear the current selection on this layer featureSelection.Clear(); activeView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, map, null); // Get the OIDS we traversed that we want to select ILongArray longArray = sourceOIDs[sourceIndex]; if (longArray.Count > 0) { // We have some OIDs, ad them to the selection set ISelectionSet selectionSet = featureSelection.SelectionSet; // Transfer from LongArray to an array of longs int numIDs = longArray.Count; int[] oids = new int[numIDs]; for (int oidIndex = oids.GetLowerBound(0); oidIndex < numIDs; oidIndex++) selectionSet.Add(longArray.get_Element(oidIndex)); } } } } layer = enumLayer.Next(); } // Refresh the selection and Map ISelectionEvents selectionEvents = (ISelectionEvents)map; selectionEvents.SelectionChanged(); activeView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, map, null); } /// <summary> /// This is a recursive function that simply finds the connected traversal elements. /// It adds the OID of the feature to the appropriate array of ObjectIDs. /// </summary> private void OutputConnected(INATraversalResultQuery naTraversalResultQuery, INATraversalResultElement inputElement, esriRelDirection searchDirection, ILongArray[] sourceOIDs) { IFeatureCursor featureCursor; if (inputElement.ElementType == esriNetworkElementType.esriNETJunction) featureCursor = naTraversalResultQuery.SearchConnected(inputElement, esriNetworkElementType.esriNETEdge, searchDirection, false); else featureCursor = naTraversalResultQuery.SearchConnected(inputElement, esriNetworkElementType.esriNETJunction, searchDirection, false); // Iterate through the connected features. // For each feature process it and then recursively call this function again // to process the connected features. INATraversalResultElement connectedElement = (INATraversalResultElement)featureCursor.NextFeature(); while (connectedElement != null) { if (sourceOIDs[connectedElement.SourceID] != null) sourceOIDs[connectedElement.SourceID].Add(connectedElement.SourceOID); // Recursion OutputConnected(naTraversalResultQuery, connectedElement, searchDirection, sourceOIDs); connectedElement = (INATraversalResultElement)featureCursor.NextFeature(); } } } }
[Visual Basic .NET]
SelectFeaturesTool.vb
Imports System Imports System.Windows.Forms Imports System.Runtime.InteropServices Imports ESRI.ArcGIS.Framework Imports ESRI.ArcGIS.NetworkAnalyst Imports ESRI.ArcGIS.Carto Imports ESRI.ArcGIS.esriSystem Imports ESRI.ArcGIS.Geometry Imports ESRI.ArcGIS.Geodatabase Imports ESRI.ArcGIS.NetworkAnalystUI Public Class SelectFeaturesTool Inherits ESRI.ArcGIS.Desktop.AddIns.Tool Dim m_networkAnalystExtension As INetworkAnalystExtension Public Sub New() m_networkAnalystExtension = My.ArcMap.Application.FindExtensionByName("Network Analyst") End Sub Protected Overrides Sub OnUpdate() Enabled = My.ArcMap.Application IsNot Nothing End Sub Private Function GetActiveAnalysisLayer() As INALayer If Not (m_networkAnalystExtension Is Nothing) Then Return m_networkAnalystExtension.NAWindow.ActiveAnalysis Else Return Nothing End If End Function Protected Overloads Overrides Function OnDeactivate() As Boolean Return True End Function ''' <summary> ''' Finds the closest feature to the point clicked that corresponds to a NATraversalResultElement in the active analysis layer's traversal result ''' </summary> Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs) Try ' Get the current network analysis traversal result Dim naLayer As INALayer = GetActiveAnalysisLayer() If (naLayer Is Nothing) Then Throw New Exception("Null NALayer") End If Dim naTraversalResult As INATraversalResult = CType(naLayer.Context.Result, INATraversalResult) If (naTraversalResult Is Nothing) Then Throw New Exception("Active analysis layer does not have a result") End If Dim naResult = CType(naTraversalResult, INAResult) If (Not naResult.HasValidResult) Then Throw New Exception("Active analysis layer has an invalid result.") End If ' Get the current map Dim map As IMap = My.ArcMap.Document.FocusMap Dim activeView As IActiveView = My.ArcMap.Document.ActiveView ' Get the current point and buffer it into a polygon based on the search tolerance Dim point As IPoint = activeView.ScreenDisplay.DisplayTransformation.ToMapPoint(e.X, e.Y) Dim proximityOperator As IProximityOperator = point Dim topologicalOperator As ITopologicalOperator = point Dim polygon As IPolygon = topologicalOperator.Buffer(activeView.Extent.Width / 20) Dim closestDistance As Double = activeView.Extent.Width / 20 Dim closestFeature As IFeature = Nothing ' Setup the spatial filter Dim spatialFilter As ISpatialFilter = New SpatialFilterClass() spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelEnvelopeIntersects spatialFilter.Geometry = polygon.Envelope 'Loop through the layers in the map that participate in the network dataset 'Find the closest feature Dim layers As IEnumLayer = map.Layers(Nothing, True) Dim layer As ILayer = layers.Next() While Not layer Is Nothing If TypeOf layer Is IFeatureLayer Then Dim featureLayer As IFeatureLayer = layer If featureLayer.Visible Then Dim featureClass As IFeatureClass = featureLayer.FeatureClass ' loop though all the sources in the network, trying to see if it corresponds to a source in the traversal result. Dim sourceIndex As Integer For sourceIndex = 0 To naTraversalResult.SourceCount - 1 ' Check to see if the feature class matches the traversal result source's feature class Dim currentTable As ITable = featureClass Dim sourceTable As ITable = naTraversalResult.Source(sourceIndex).Table If currentTable Is sourceTable Then ' Since we correspond to a source in the traversal result, find the closest feature to the point clicked. spatialFilter.GeometryField = featureClass.ShapeFieldName Dim featureCursor As IFeatureCursor = featureClass.Search(spatialFilter, False) Dim feature As IFeature = featureCursor.NextFeature() While Not feature Is Nothing Dim distance As Double = proximityOperator.ReturnDistance(feature.Shape) If distance < closestDistance Then closestFeature = feature closestDistance = distance End If feature = featureCursor.NextFeature() End While End If Next End If End If layer = layers.Next() End While If closestFeature Is Nothing Then MessageBox.Show("Could not find a feature") Else SelectTraversedFeatures(closestFeature) 'Since we found a feature, select the features traversed to get to it. End If Catch exception As Exception MessageBox.Show(exception.Message, "Error") End Try End Sub ''' <summary> ''' Selects all the features in the visible feature layers that correspond to NATraversalResultElements ''' that were traversed to get to the selected feature. Do this by using INATraversalResultQuery to walk ''' the traversal result. ''' </summary> Private Sub SelectTraversedFeatures(ByVal selectedFeature As IFeature) ' Get reference to the current map Dim map As IMap = My.ArcMap.Document.FocusMap ' Get the NATraversalResult Dim naLayer As INALayer = GetActiveAnalysisLayer() Dim naContext As INAContext = naLayer.Context Dim naTraversalResult As INATraversalResult = CType(naContext.Result, INATraversalResult) Dim naTraversalResultEdit As INATraversalResultEdit = CType(naTraversalResult, INATraversalResultEdit) Dim naTraversalResultQuery As INATraversalResultQuery = CType(naTraversalResult, INATraversalResultQuery) ' Cache the FeatureClass of each input source ' Also create a LongArray to hold the OIDs of features traversed ' This makes the assumption that the sources go from 1 to N Dim selectedSource As INATraversalResultSource = Nothing Dim sourceFeatureClasses As IFeatureClass() = New IFeatureClass(naTraversalResult.SourceCount + 1) {} Dim sourceOIDs As ILongArray() = New ILongArray(naTraversalResult.SourceCount + 1) {} Dim sourceIndex As Integer For sourceIndex = sourceFeatureClasses.GetLowerBound(0) To sourceFeatureClasses.GetUpperBound(0) Dim naTraversalResultSource As INATraversalResultSource = naTraversalResult.SourceByID(sourceIndex) If Not naTraversalResultSource Is Nothing Then Dim featureClass As IFeatureClass = naTraversalResultSource.Table If Not featureClass Is Nothing Then sourceFeatureClasses(sourceIndex) = featureClass ' If this source is the one of the selected feature, ' remember it for later when we find it's traversal elements If featureClass Is selectedFeature.Class Then selectedSource = naTraversalResultSource End If sourceOIDs(sourceIndex) = New LongArrayClass() End If End If Next ' Verify the selected feature's class is actually in the network If selectedSource Is Nothing Then Throw (New Exception("The selected feature is not part of the network.")) End If ' Get the traversal edges corresponding to the selected feature ' There may be more than one returned if there are multiple facilities ' or if the edge was traversed in both directions. Dim queryFilter As IQueryFilter = New QueryFilterClass() queryFilter.WhereClause = "SourceID = " & selectedSource.ID & " and SourceOID = " & selectedFeature.OID ' Figure out if the selected feature is a point or line ' and get the correct traversal feature class Dim traversalFClass As IFeatureClass If selectedFeature.Shape.GeometryType = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPoint Then traversalFClass = naTraversalResultQuery.FeatureClass(esriNetworkElementType.esriNETJunction) ElseIf selectedFeature.Shape.GeometryType = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline Then traversalFClass = naTraversalResultQuery.FeatureClass(esriNetworkElementType.esriNETEdge) Else Throw (New Exception("You must start from a point or polyline")) End If If traversalFClass.FeatureCount(Nothing) = 0 Then Throw (New Exception("The traversal result is empty. Please click solve.")) End If ' Search through the traversal class to find those corresponding to the selected feature Dim naTraversalResultElement As INATraversalResultElement Dim featureCursor As IFeatureCursor = traversalFClass.Search(queryFilter, True) Dim feature As IFeature = featureCursor.NextFeature() If feature Is Nothing Then Throw (New Exception("You must pick a feature that was traversed.")) End If ' Loop through all the traversal result elements for that feature While Not feature Is Nothing naTraversalResultElement = CType(feature, INATraversalResultElement) sourceOIDs(naTraversalResultElement.SourceID).Add(naTraversalResultElement.SourceOID) 'Get the connected traversal elements going backwards OutputConnected(naTraversalResultQuery, naTraversalResultElement, esriRelDirection.esriRelDirectionBackward, sourceOIDs) feature = featureCursor.NextFeature() End While ' Select the features in the Map SelectFeatures(map, sourceFeatureClasses, sourceOIDs) End Sub ''' <summary> ''' This function selects the features corresponding to the arrays of feature classes and OIDs ''' </summary> Private Sub SelectFeatures(ByVal map As IMap, ByVal sourceFeatureClasses() As IFeatureClass, ByVal sourceOIDs() As ILongArray) Dim layers As IEnumLayer = map.Layers(Nothing, True) Dim layer As ILayer = layers.Next() Dim activeView As IActiveView = CType(map, IActiveView) While Not layer Is Nothing If (TypeOf layer Is IFeatureLayer) And (TypeOf layer Is IFeatureSelection) Then Dim featureLayer As IFeatureLayer = layer Dim featureSelection As IFeatureSelection = layer If featureLayer.Visible And featureLayer.Selectable Then Dim sourceIndex As Integer For sourceIndex = sourceFeatureClasses.GetLowerBound(0) To sourceFeatureClasses.GetUpperBound(0) If featureLayer.FeatureClass Is sourceFeatureClasses(sourceIndex) Then 'Clear the current selection on this layer featureSelection.Clear() activeView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, map, Nothing) ' Get the OIDS we traversed that we want to select Dim longArray As ILongArray = sourceOIDs(sourceIndex) If longArray.Count > 0 Then ' We have some OIDs, ad them to the selection set Dim selectionSet As ISelectionSet = featureSelection.SelectionSet ' Transfer from LongArray to an array of longs Dim numIDs As Integer = longArray.Count Dim oids() As Integer = New Integer(numIDs) {} Dim oidIndex As Integer For oidIndex = oids.GetLowerBound(0) To numIDs - 1 selectionSet.Add(longArray.Element(oidIndex)) Next End If End If Next End If End If layer = layers.Next() End While ' Refresh the selection and Map Dim selectionEvents As ISelectionEvents = CType(map, ISelectionEvents) selectionEvents.SelectionChanged() activeView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, map, Nothing) End Sub ''' <summary> ''' This is a recursive function that simply finds the connected traversal elements. ''' It adds the OID of the feature to the appropriate array of ObjectIDs. ''' </summary> Private Sub OutputConnected(ByVal naTraversalResultQuery As INATraversalResultQuery, ByVal inputElement As INATraversalResultElement, ByVal searchDirection As esriRelDirection, ByVal sourceOIDs As ILongArray()) Dim featureCursor As IFeatureCursor If inputElement.ElementType = esriNetworkElementType.esriNETJunction Then featureCursor = naTraversalResultQuery.SearchConnected(inputElement, esriNetworkElementType.esriNETEdge, searchDirection, False) Else featureCursor = naTraversalResultQuery.SearchConnected(inputElement, esriNetworkElementType.esriNETJunction, searchDirection, False) End If Dim connectedElement As INATraversalResultElement = CType(featureCursor.NextFeature, INATraversalResultElement) While Not (connectedElement Is Nothing) If Not (sourceOIDs(connectedElement.SourceID) Is Nothing) Then sourceOIDs(connectedElement.SourceID).Add(connectedElement.SourceOID) End If OutputConnected(naTraversalResultQuery, connectedElement, searchDirection, sourceOIDs) connectedElement = CType(featureCursor.NextFeature, INATraversalResultElement) End While End Sub End Class