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