ArcObjects Library Reference  

AddTraversalResultsToMap

About the Add a traversal result to the map Sample

[C#]

AddTraversalResultsToMap.cs

//*************************************************************************************
//       ArcGIS Network Analyst extension - Add Traversal Results to Map Sample
//
//   This simple code is an ArcGIS Add-In that shows how to take the traversal results 
//     from a solved network analysis layer and add them to the map as feature layers.
//     The user can then step through the traversal results layers to see the 
//     individual features that make up the results.
//
//************************************************************************************

using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.CartoUI;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.NetworkAnalyst;
using ESRI.ArcGIS.NetworkAnalystUI;

namespace NAAddTraversalResultToMap
{
	public class AddTraversalResultsToMap : ESRI.ArcGIS.Desktop.AddIns.Button
	{
		public AddTraversalResultsToMap()
		{
		}

		/// <summary>
		/// OnClick is the main function for the add-in.  When the button is clicked in ArcMap,
		///  this code will execute.  Note that if you re-solve the analysis layer associated with 
		///  the traversal result, any open attribute tables associated with the traversal result
		///  will disconnect and need to be reopened.
		/// </summary>
		protected override void OnClick()
		{
			try
			{
				var networkAnalystExtension = ArcMap.Application.FindExtensionByName("Network Analyst") as INetworkAnalystExtension;
				if (networkAnalystExtension == null)
					throw new System.Exception("Network Analyst Extension is not available.");

				INALayer naLayer = networkAnalystExtension.NAWindow.ActiveAnalysis;
				if (naLayer == null)
					throw new System.Exception("Cannot add a traversal result.  There is no active network analysis layer.");

				var result = naLayer.Context.Result;
				if (result == null || !result.HasValidResult)
					throw new System.Exception("Cannot add a traversal result.  The active analysis layer either does not have a valid result or does not support traversal results.");

				// In the case of Vehicle Routing Problem (VRP) layers, get the traversal result from the internal route context, if available
				var vrpResult = result as INAVRPResult;
				if (vrpResult != null)
				{
					if (vrpResult.InternalRouteContext == null || vrpResult.InternalRouteContext.Result == null || !vrpResult.InternalRouteContext.Result.HasValidResult)
						throw new System.Exception("Cannot add a traversal result.  VRP layers cannot have a shape type of none.");

					result = vrpResult.InternalRouteContext.Result;
				}

				var naTraversalResultEdit = result as INATraversalResultEdit;
				if (naTraversalResultEdit == null)
					throw new System.Exception("Cannot add a traversal result.  The active analysis layer does not support traversal results.");

				// Geometry needs to be inferred for traversal edges in cases where the route geometry is not already generated.
				//  It doesn't hurt to make this call, though, even when a geometry is already present.
				naTraversalResultEdit.InferGeometry(string.Empty, null, new CancelTrackerClass());

				// Load the junction, edge and turn traversal results to the map
				// also add the internal route and internal NAContext if available
				IGroupLayer groupLayer = new GroupLayerClass();
				groupLayer.Name = "NAResults - " + ((ILayer)naLayer).Name;
				AddNATraversalResultLayersToGroup(groupLayer, result);

				// The newly added layer references in-memory features of the NALayer.
				//  Therefore, in order for the new layer to persist properly, it has to be 
				//  placed below the layer it references.
				var mapLayers = ArcMap.Document.FocusMap as IMapLayers2;
				int naLayerPosition = GetLayerIndex(naLayer as ILayer);
				mapLayers.InsertLayer(groupLayer, false, naLayerPosition + 1);
			}
			catch (System.Exception e)
			{
				System.Windows.Forms.MessageBox.Show(e.Message, "Add-In Exception", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
			}
		}

		/// <summary>
		/// AddNATraversalResultLayersToGroup will take the traversal results from an INAResult and add them to the map as a group layer
		/// </summary>
		private void AddNATraversalResultLayersToGroup(IGroupLayer groupLayer, INAResult result)
		{
			var naTraversalResultQuery = result as INATraversalResultQuery2;
			groupLayer.Add(GetTraversalResultLayer(esriNetworkElementType.esriNETJunction, naTraversalResultQuery));
			groupLayer.Add(GetTraversalResultLayer(esriNetworkElementType.esriNETTurn, naTraversalResultQuery));
			groupLayer.Add(GetTraversalResultLayer(esriNetworkElementType.esriNETEdge, naTraversalResultQuery));
		}

		/// <summary>
		/// GetTraversalResultLayer will generate a layer out of the traversal result for the specified element type
		/// </summary>
		private IFeatureLayer GetTraversalResultLayer(esriNetworkElementType elementType, INATraversalResultQuery2 naTraversalResultQuery)
		{
			//Junctions Traversal Result Feature Layer
			IFeatureClass traversalResultFeatureClass = naTraversalResultQuery.get_FeatureClass(elementType);
			if (traversalResultFeatureClass != null)
			{
				// save the rows in this class out when the MXD is saved
				INAClass naClass = traversalResultFeatureClass as INAClass;
				naClass.SaveRowsOnPersist = true;

				// create the traversal result layer
				IFeatureLayer traversalResultLayer = new FeatureLayerClass();
				traversalResultLayer.FeatureClass = traversalResultFeatureClass;
				traversalResultLayer.Name = traversalResultFeatureClass.AliasName;

				// Set up the layer with an appropriate symbology
				var geoFeatureLayer = traversalResultLayer as IGeoFeatureLayer;
				geoFeatureLayer.RendererPropertyPageClassID = (new SingleSymbolPropertyPageClass()).ClassID;
				geoFeatureLayer.Renderer = GetRenderer(elementType);

				return traversalResultLayer;
			}
			return null;
		}

		/// <summary>
		/// GetRenderer will return a feature renderer with symbology appropriate to the specified element type
		/// </summary>
		private IFeatureRenderer GetRenderer(esriNetworkElementType networkElementType)
		{
			ISimpleRenderer simpleRend = new SimpleRendererClass();
			IRgbColor color = new RgbColorClass();

			switch (networkElementType)
			{
				// The junction symbology will be a large yellow circle with a thick black outline
				case esriNetworkElementType.esriNETJunction:

					ISimpleMarkerSymbol junctionPointSymbol = new SimpleMarkerSymbolClass();
					junctionPointSymbol.Style = esriSimpleMarkerStyle.esriSMSCircle;
					junctionPointSymbol.Size = 10;

					//yellow
					color.Red = 255;
					color.Blue = 0;
					color.Green = 255;
					junctionPointSymbol.Color = color;

					junctionPointSymbol.Outline = true;
					junctionPointSymbol.OutlineSize = 2;

					//black
					color.Red = 0;
					color.Blue = 0;
					color.Green = 0;
					junctionPointSymbol.OutlineColor = color;

					simpleRend.Label = "TRFC_Junctions";
					simpleRend.Symbol = junctionPointSymbol as ISymbol;
					return simpleRend as IFeatureRenderer;

				// The turn symbology will be a thick purple line
				case esriNetworkElementType.esriNETTurn:

					ISimpleLineSymbol turnLineSymbol = new SimpleLineSymbolClass();
					turnLineSymbol.Style = esriSimpleLineStyle.esriSLSSolid;
					turnLineSymbol.Width = 4;

					//purple
					color.Red = 125;
					color.Blue = 125;
					color.Green = 0;
					turnLineSymbol.Color = color;

					simpleRend.Label = "TRFC_Turns";
					simpleRend.Symbol = turnLineSymbol as ISymbol;
					return simpleRend as IFeatureRenderer;

				// The edge symbology will be a thick blue line
				case esriNetworkElementType.esriNETEdge:

					ISimpleLineSymbol edgeLineSymbol = new SimpleLineSymbolClass();
					edgeLineSymbol.Style = esriSimpleLineStyle.esriSLSSolid;
					edgeLineSymbol.Width = 4;

					//blue
					color.Red = 0;
					color.Blue = 255;
					color.Green = 0;
					edgeLineSymbol.Color = color;

					simpleRend.Label = "TRFC_Edges";
					simpleRend.Symbol = edgeLineSymbol as ISymbol;
					return simpleRend as IFeatureRenderer;
			}
			return null;
		}

		private int GetLayerIndex(ILayer layer)
		{
			for (int index = 0; index < ArcMap.Document.FocusMap.LayerCount; index++)
			{
				ILayer layerAtIndex = ArcMap.Document.FocusMap.get_Layer(index);
				if (layerAtIndex == layer)
					return index;
			}
			return -1;
		}

		protected override void OnUpdate()
		{
			Enabled = ArcMap.Application != null;
		}
	}
}

[Visual Basic .NET]

AddTraversalResultsToMap.vb

'*************************************************************************************
'       ArcGIS Network Analyst extension - Add Traversal Results to Map Sample
'
'   This simple code is an ArcGIS Add-In that shows how to take the traversal results 
'     from a solved network analysis layer and add them to the map as feature layers.
'     The user can then step through the traversal results layers to see the 
'     individual features that make up the results.
'
'************************************************************************************


Imports Microsoft.VisualBasic
Imports System
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Display
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.NetworkAnalyst
Imports ESRI.ArcGIS.NetworkAnalystUI

Namespace NAAddTraversalResultToMap
	Public Class AddTraversalResultsToMap
		Inherits ESRI.ArcGIS.Desktop.AddIns.Button
		Public Sub New()
		End Sub

		''' <summary>
		''' OnClick is the main function for the add-in.  When the button is clicked in ArcMap,
		'''  this code will execute.  Note that if you re-solve the analysis layer associated with 
		'''  the traversal result, any open attribute tables associated with the traversal result
		'''  will disconnect and need to be reopened.
		''' </summary>
		Protected Overrides Sub OnClick()
			Try
				Dim networkAnalystExtension As INetworkAnalystExtension = TryCast(ArcMap.Application.FindExtensionByName("Network Analyst"), INetworkAnalystExtension)
				If networkAnalystExtension Is Nothing Then
					Throw New System.Exception("Network Analyst Extension is not available.")
				End If

				Dim naLayer As INALayer = networkAnalystExtension.NAWindow.ActiveAnalysis
				If naLayer Is Nothing Then
					Throw New System.Exception("Cannot add a traversal result.  There is no active network analysis layer.")
				End If

				Dim result As INAResult = naLayer.Context.Result
				If result Is Nothing OrElse (Not result.HasValidResult) Then
					Throw New System.Exception("Cannot add a traversal result.  The active analysis layer either does not have a valid result or does not support traversal results.")
				End If

				' In the case of Vehicle Routing Problem (VRP) layers, get the traversal result from the internal route context, if available
				Dim vrpResult As INAVRPResult = TryCast(result, INAVRPResult)
				If vrpResult IsNot Nothing Then
					If vrpResult.InternalRouteContext Is Nothing OrElse vrpResult.InternalRouteContext.Result Is Nothing OrElse Not vrpResult.InternalRouteContext.Result.HasValidResult Then
						Throw New System.Exception("Cannot add a traversal result.  VRP layers cannot have a shape type of none.")
					End If

					result = vrpResult.InternalRouteContext.Result
				End If

				Dim naTraversalResultEdit As INATraversalResultEdit = TryCast(result, INATraversalResultEdit)
				If naTraversalResultEdit Is Nothing Then
					Throw New System.Exception("Cannot add a traversal result.  The active analysis layer does not support traversal results.")
				End If

				' Geometry needs to be inferred for traversal edges in cases where the route geometry is not already generated.
				'  It doesn't hurt to make this call, though, even when a geometry is already present.
				naTraversalResultEdit.InferGeometry(String.Empty, Nothing, New CancelTrackerClass())

				' Load the junction, edge and turn traversal results to the map
				' also add the internal route and internal NAContext if available
				Dim groupLayer As IGroupLayer = New GroupLayerClass()
				groupLayer.Name = "NAResults - " & DirectCast(naLayer, ILayer).Name
				AddNATraversalResultLayersToGroup(groupLayer, result)

				' The newly added layer references in-memory features of the NALayer.
				'  Therefore, in order for the new layer to persist properly, it has to be 
				'  placed below the layer it references.
				Dim mapLayers As IMapLayers2 = TryCast(ArcMap.Document.FocusMap, IMapLayers2)
				Dim naLayerPosition As Integer = GetLayerIndex(TryCast(naLayer, ILayer))
				mapLayers.InsertLayer(groupLayer, False, naLayerPosition + 1)

			Catch e As System.Exception
				System.Windows.Forms.MessageBox.Show(e.Message, "Add-In Exception", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.[Error])
			End Try
		End Sub

		''' <summary>
		''' AddFeatureLayerToMap will take the traversal result from an NAResult and add it to the map as a layer
		''' </summary>
		Private Sub AddFeatureLayerToMap(ByVal result As INAResult, ByVal naLayerName As String, ByVal naLayerPosition As Integer, ByVal elementType As esriNetworkElementType)
			Dim naTraversalResultQuery As INATraversalResultQuery = TryCast(result, ESRI.ArcGIS.NetworkAnalyst.INATraversalResultQuery)

			' Note that the traversal result layer that is being created will update each time the referenced layer
			'  completes another solve.
			Dim featureLayer As IFeatureLayer = New FeatureLayerClass()
			featureLayer.FeatureClass = naTraversalResultQuery.FeatureClass(elementType)
			featureLayer.Name = naLayerName & " - Traversal Result " & featureLayer.FeatureClass.AliasName

			' The newly added layer references in-memory features of the NALayer.
			'  Therefore, in order for the new layer to persist properly, it has to be 
			'  placed below the layer it references.
			Dim mapLayers As IMapLayers2 = TryCast(ArcMap.Document.FocusMap, IMapLayers2)
			mapLayers.InsertLayer(featureLayer, False, naLayerPosition + 1)
		End Sub

		''' <summary>
		''' AddNATraversalResultLayersToGroup will take the traversal results from an INAResult and add them to the map as a group layer
		''' </summary>
		Private Sub AddNATraversalResultLayersToGroup(ByVal groupLayer As IGroupLayer, ByVal result As INAResult)
			Dim naTraversalResultQuery As INATraversalResultQuery2 = TryCast(result, INATraversalResultQuery2)
			groupLayer.Add(GetTraversalResultLayer(esriNetworkElementType.esriNETJunction, naTraversalResultQuery))
			groupLayer.Add(GetTraversalResultLayer(esriNetworkElementType.esriNETTurn, naTraversalResultQuery))
			groupLayer.Add(GetTraversalResultLayer(esriNetworkElementType.esriNETEdge, naTraversalResultQuery))
		End Sub

		''' <summary>
		''' GetTraversalResultLayer will generate a layer out of the traversal result for the specified element type
		''' </summary>
		Private Function GetTraversalResultLayer(ByVal elementType As esriNetworkElementType, ByVal naTraversalResultQuery As INATraversalResultQuery2) As IFeatureLayer
			'Junctions Traversal Result Feature Layer
			Dim traversalResultFeatureClass As IFeatureClass = naTraversalResultQuery.FeatureClass(elementType)
			If traversalResultFeatureClass IsNot Nothing Then
				' save the rows in this class out when the MXD is saved
				Dim naClass As INAClass = TryCast(traversalResultFeatureClass, INAClass)
				naClass.SaveRowsOnPersist = True

				' create the traversal result layer
				Dim traversalResultLayer As IFeatureLayer = New FeatureLayerClass()
				traversalResultLayer.FeatureClass = traversalResultFeatureClass
				traversalResultLayer.Name = traversalResultFeatureClass.AliasName

				' Set up the layer with an appropriate symbology
				Dim geoFeatureLayer As IGeoFeatureLayer = TryCast(traversalResultLayer, IGeoFeatureLayer)
				geoFeatureLayer.RendererPropertyPageClassID = (New ESRI.ArcGIS.CartoUI.SingleSymbolPropertyPageClass()).ClassID
				geoFeatureLayer.Renderer = GetRenderer(elementType)

				Return traversalResultLayer
			End If
			Return Nothing
		End Function

		''' <summary>
		''' GetRenderer will return a feature renderer with symbology appropriate to the specified element type
		''' </summary>
		Private Function GetRenderer(ByVal networkElementType As esriNetworkElementType) As IFeatureRenderer
			Dim simpleRend As ISimpleRenderer = New SimpleRendererClass()
			Dim color As IRgbColor = New RgbColorClass()

			Select Case networkElementType
				' The junction symbology will be a large yellow circle with a thick black outline
				Case esriNetworkElementType.esriNETJunction

					Dim junctionPointSymbol As ISimpleMarkerSymbol = New SimpleMarkerSymbolClass()
					junctionPointSymbol.Style = ESRI.ArcGIS.Display.esriSimpleMarkerStyle.esriSMSCircle
					junctionPointSymbol.Size = 10

					'yellow
					color.Red = 255
					color.Blue = 0
					color.Green = 255
					junctionPointSymbol.Color = color

					junctionPointSymbol.Outline = True
					junctionPointSymbol.OutlineSize = 2

					'black
					color.Red = 0
					color.Blue = 0
					color.Green = 0
					junctionPointSymbol.OutlineColor = color

					simpleRend.Label = "TRFC_Junctions"
					simpleRend.Symbol = TryCast(junctionPointSymbol, ISymbol)
					Return TryCast(simpleRend, IFeatureRenderer)

					' The turn symbology will be a thick purple line
				Case esriNetworkElementType.esriNETTurn

					Dim turnLineSymbol As ISimpleLineSymbol = New SimpleLineSymbolClass()
					turnLineSymbol.Style = ESRI.ArcGIS.Display.esriSimpleLineStyle.esriSLSSolid
					turnLineSymbol.Width = 4

					'purple
					color.Red = 125
					color.Blue = 125
					color.Green = 0
					turnLineSymbol.Color = color

					simpleRend.Label = "TRFC_Turns"
					simpleRend.Symbol = TryCast(turnLineSymbol, ISymbol)
					Return TryCast(simpleRend, IFeatureRenderer)

					' The edge symbology will be a thick blue line
				Case esriNetworkElementType.esriNETEdge

					Dim edgeLineSymbol As ISimpleLineSymbol = New SimpleLineSymbolClass()
					edgeLineSymbol.Style = ESRI.ArcGIS.Display.esriSimpleLineStyle.esriSLSSolid
					edgeLineSymbol.Width = 4

					'blue
					color.Red = 0
					color.Blue = 255
					color.Green = 0
					edgeLineSymbol.Color = color

					simpleRend.Label = "TRFC_Edges"
					simpleRend.Symbol = TryCast(edgeLineSymbol, ISymbol)
					Return TryCast(simpleRend, IFeatureRenderer)
			End Select
			Return Nothing
		End Function

		Private Function GetLayerIndex(ByVal layer As ILayer) As Integer
			For index As Integer = 0 To ArcMap.Document.FocusMap.LayerCount - 1
				Dim layerAtIndex As ILayer = ArcMap.Document.FocusMap.Layer(index)
				If layerAtIndex Is layer Then
					Return index
				End If
			Next index

			Return -1
		End Function

		Protected Overrides Sub OnUpdate()
			Enabled = Not ArcMap.Application Is Nothing
		End Sub

	End Class

End Namespace