ArcObjects Library Reference  

frmServiceAreaSolver

About the Service area solver Sample

[C#]

frmServiceAreaSolver.cs

using System;
using System.Windows.Forms;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.NetworkAnalyst;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using System.Text;

namespace ServiceAreaSolver
{
	public partial class frmServiceAreaSolver : Form
	{
		private INAContext m_NAContext;

		#region Main Form Constructor and Setup

        /// <summary>
        /// Initialize the solver by calling the ArcGIS Network Analyst extension functions.
        /// </summary>
        public frmServiceAreaSolver()
        {
            InitializeComponent();
            Initialize();
        }

        /// <summary>
        /// Set up the default values on the form
        /// </summary>
        private void Initialize()
		{
			txtCutOff.Text = "5";
			lstOutput.Items.Clear();
			cbCostAttribute.Items.Clear();
			ckbUseRestriction.Checked = false;
			axMapControl.ClearLayers();

			txtWorkspacePath.Text = Application.StartupPath + @"\..\..\..\..\..\Data\SanFrancisco\SanFrancisco.gdb";
			txtNetworkDataset.Text = "Streets_ND";
			txtFeatureDataset.Text = "Transportation";
			txtInputFacilities.Text = "Hospitals";
			gbServiceAreaSolver.Enabled = false;
		}

		#endregion

		#region Button Clicks

        /// <summary>
        /// Call the Service Area solver and display the results
        /// </summary>
        /// <param name="sender">Sender of the event</param>
        /// <param name="e">Event</param>
        private void btnSolve_Click(object sender, EventArgs e)
		{
			this.Cursor = Cursors.WaitCursor;
			lstOutput.Items.Clear();

            IGPMessages gpMessages = new GPMessagesClass();
            try
            {
                lstOutput.Items.Clear();
                lstOutput.Items.Add("Solving...");

                ConfigureSolverSettings();

				if (m_NAContext.Solver.Solve(m_NAContext, gpMessages, null))
			        lstOutput.Items.Add("Partial Solve Generated.");

                DisplayOutput();

            }
			catch (Exception ee)
			{
                lstOutput.Items.Add("Failure: " + ee.Message);
            }

            lstOutput.Items.Add(GetGPMessagesAsString(gpMessages));

            RefreshMapDisplay();

			this.Cursor = Cursors.Default;
		}

        /// <summary>
        /// Open the network dataset and set up the map
        /// </summary>
        /// <param name="sender">Sender of the event</param>
        /// <param name="e">Event</param>
        private void btnLoadMap_Click(object sender, EventArgs e)
		{
			this.Cursor = Cursors.WaitCursor;

			gbServiceAreaSolver.Enabled = false;
			lstOutput.Items.Clear();

            // Verify that the workspace is valid
			IWorkspace workspace = OpenWorkspace(txtWorkspacePath.Text);
            if (workspace != null)
            {
                // Open the network dataset and generate a solver/context
                INetworkDataset networkDataset = OpenNetworkDataset(workspace, txtFeatureDataset.Text, txtNetworkDataset.Text);
                IFeatureWorkspace featureWorkspace = workspace as IFeatureWorkspace;
                CreateSolverContext(networkDataset);
                if (m_NAContext != null)
                {
                    LoadCostAttributes(networkDataset);
                    if (LoadLocations(featureWorkspace))
                    {
                        AddNetworkDatasetLayerToMap(networkDataset);
                        AddNetworkAnalysisLayerToMap();

                        // work around a transparency issue
                        IGeoDataset geoDataset = networkDataset as IGeoDataset;
                        axMapControl.Extent = axMapControl.FullExtent;
                        axMapControl.Extent = geoDataset.Extent;

                        if (m_NAContext != null) gbServiceAreaSolver.Enabled = true;
                    }
                }
            }

			this.Cursor = Cursors.Default;
		}

		#endregion

		#region Set up Context and Solver

        /// <summary>
        /// Geodatabase function: open work space
        /// </summary>
        /// <param name="strGDBName">Input file name</param>
        /// <returns>Workspace</returns>
        public IWorkspace OpenWorkspace(string strGDBName)
		{
			// As Workspace Factories are Singleton objects, they must be instantiated with the Activator
			var workspaceFactory = System.Activator.CreateInstance(System.Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory")) as ESRI.ArcGIS.Geodatabase.IWorkspaceFactory;

            if (!System.IO.Directory.Exists(strGDBName))
			{
                MessageBox.Show("The workspace: " + strGDBName + " does not exist", "Workspace Error");
				return null;
			}

			IWorkspace workspace = null;
			try
			{
                workspace = workspaceFactory.OpenFromFile(strGDBName, 0);
			}
			catch (Exception ex)
			{
				MessageBox.Show("Opening workspace failed: " + ex.Message, "Workspace Error");
			}

			return workspace;
		}

        /// <summary>
        /// Geodatabase function: open network dataset
        /// </summary>
        /// <param name="workspace">Input workspace</param>
        /// <param name="strNDSName">Input network dataset name</param>
        /// <returns>NetworkDataset</returns>
        public INetworkDataset OpenNetworkDataset(IWorkspace workspace, string featureDatasetName, string strNDSName)
		{
			// Obtain the dataset container from the workspace
			var featureWorkspace = workspace as IFeatureWorkspace;
			ESRI.ArcGIS.Geodatabase.IFeatureDataset featureDataset = featureWorkspace.OpenFeatureDataset(featureDatasetName);
			var featureDatasetExtensionContainer = featureDataset as ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtensionContainer;
			ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtension featureDatasetExtension = featureDatasetExtensionContainer.FindExtension(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset);
			var datasetContainer3 = featureDatasetExtension as ESRI.ArcGIS.Geodatabase.IDatasetContainer3;

			// Use the container to open the network dataset.
			ESRI.ArcGIS.Geodatabase.IDataset dataset = datasetContainer3.get_DatasetByName(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset, strNDSName);
			return dataset as ESRI.ArcGIS.Geodatabase.INetworkDataset;
		}

        /// <summary>
        /// Geodatabase function: get network dataset
        /// </summary>
        /// <param name="networkDataset">Input network dataset</param>
        /// <returns>DE network dataset</returns>		
        public IDENetworkDataset GetDENetworkDataset(INetworkDataset networkDataset)
        {
            // Cast from the network dataset to the DatasetComponent
            IDatasetComponent dsComponent = networkDataset as IDatasetComponent;

            // Get the data element
            return dsComponent.DataElement as IDENetworkDataset;
        }

        /// <summary>
        /// Create NASolver and NAContext
        /// </summary>
        /// <param name="networkDataset">Input network dataset</param>
        private void CreateSolverContext(INetworkDataset networkDataset)
		{
			if (networkDataset == null) return;

            //Get the Data Element
            IDENetworkDataset deNDS = GetDENetworkDataset(networkDataset);

            INASolver naSolver = new NAServiceAreaSolverClass();
            m_NAContext = naSolver.CreateContext(deNDS, naSolver.Name);
            ((INAContextEdit)m_NAContext).Bind(networkDataset, new GPMessagesClass());
        }

		#endregion

		#region Load Form Controls

        /// <summary>
        /// Find and load the cost attributes into a combo box
        /// <summary>
        private void LoadCostAttributes(INetworkDataset networkDataset)
		{
			cbCostAttribute.Items.Clear();

			int attrCount = networkDataset.AttributeCount;
			for (int attrIndex = 0; attrIndex < attrCount; attrIndex++)
			{
				INetworkAttribute networkAttribute = networkDataset.get_Attribute(attrIndex);
				if (networkAttribute.UsageType == esriNetworkAttributeUsageType.esriNAUTCost)
					cbCostAttribute.Items.Add(networkAttribute.Name);
			}

			if (cbCostAttribute.Items.Count > 0)
				cbCostAttribute.SelectedIndex = 0;
		}

        /// <summary>
        /// Find and load the cost attributes into a combo box
        /// <summary>
        /// <param name="featureWorkspace">The workspace that holds the input feature class</param>
        /// <returns>Success</returns>
        private bool LoadLocations(IFeatureWorkspace featureWorkspace)
		{
			IFeatureClass inputFeatureClass = null;
			try
			{
				inputFeatureClass = featureWorkspace.OpenFeatureClass(txtInputFacilities.Text);
			}
			catch (Exception)
			{
				MessageBox.Show("Specified input feature class does not exist");
				return false;
			}

			INamedSet classes = m_NAContext.NAClasses;
			INAClass naClass = classes.get_ItemByName("Facilities") as INAClass;

			// delete existing locations, except barriers
			naClass.DeleteAllRows();

			// Create a NAClassLoader and set the snap tolerance (meters unit)
			INAClassLoader naClassLoader = new NAClassLoaderClass();
			naClassLoader.Locator = m_NAContext.Locator;
			((INALocator3)naClassLoader.Locator).MaxSnapTolerance = 500;
			naClassLoader.NAClass = naClass;

			// Create field map to automatically map fields from input class to NAClass
			INAClassFieldMap naClassFieldMap = new NAClassFieldMapClass();
			naClassFieldMap.CreateMapping(naClass.ClassDefinition, inputFeatureClass.Fields);
			naClassLoader.FieldMap = naClassFieldMap;

			// Avoid loading network locations onto non-traversable portions of elements
			INALocator3 locator = m_NAContext.Locator as INALocator3;
			locator.ExcludeRestrictedElements = true;
			locator.CacheRestrictedElements(m_NAContext);

			// load network locations
			int rowsIn = 0;
			int rowsLocated = 0;
			naClassLoader.Load(inputFeatureClass.Search(null, true) as ICursor, null, ref rowsIn, ref rowsLocated);

			if (rowsLocated <= 0)
			{
				MessageBox.Show("Facilities were not loaded from input feature class");
				return false;
			}

			// Message all of the network analysis agents that the analysis context has changed
			INAContextEdit naContextEdit = m_NAContext as INAContextEdit;
			naContextEdit.ContextChanged();

			return true;
		}

        /// <summary>
        /// Create a layer from the context and add it to the map
        /// </summary>
        private void AddNetworkAnalysisLayerToMap()
		{
			ILayer layer = m_NAContext.Solver.CreateLayer(m_NAContext) as ILayer;
			layer.Name = m_NAContext.Solver.DisplayName;
			axMapControl.AddLayer(layer);
		}

        /// <summary>
        /// Create a layer for the network dataset and add it to the map
        /// </summary>
        private void AddNetworkDatasetLayerToMap(INetworkDataset networkDataset)
		{
			INetworkLayer networkLayer = new NetworkLayerClass();
			networkLayer.NetworkDataset = networkDataset;
			ILayer layer = networkLayer as ILayer;
			layer.Name = "Network Dataset";
			axMapControl.AddLayer(layer);
		}

		#endregion

		#region Solver Settings

        /// <summary>
        /// Prepare the solver
        /// </summary>
        private void ConfigureSolverSettings()
		{
			ConfigureSettingsSpecificToServiceAreaSolver();

			ConfigureGenericSolverSettings();

			UpdateContextAfterChangingSettings();
		}

        /// <summary>
        /// Update settings that only apply to the Service Area
        /// </summary>
        private void ConfigureSettingsSpecificToServiceAreaSolver()
		{
			INAServiceAreaSolver naSASolver = m_NAContext.Solver as INAServiceAreaSolver;

			naSASolver.DefaultBreaks = ParseBreaks(txtCutOff.Text);

			naSASolver.MergeSimilarPolygonRanges = false;
			naSASolver.OutputPolygons = esriNAOutputPolygonType.esriNAOutputPolygonSimplified;
			naSASolver.OverlapLines = true;
			naSASolver.SplitLinesAtBreaks = false;
			naSASolver.TravelDirection = esriNATravelDirection.esriNATravelDirectionFromFacility;
			naSASolver.OutputLines = esriNAOutputLineType.esriNAOutputLineNone;
		}

        /// <summary>
        /// Update settings that apply to all solvers
        /// </summary>
        private void ConfigureGenericSolverSettings()
		{
			INASolverSettings naSolverSettings = m_NAContext.Solver as INASolverSettings;
			naSolverSettings.ImpedanceAttributeName = cbCostAttribute.Text;

			// set the oneway restriction, if necessary
			IStringArray restrictions = naSolverSettings.RestrictionAttributeNames;
			restrictions.RemoveAll();
			if (ckbUseRestriction.Checked)
				restrictions.Add("Oneway");
			naSolverSettings.RestrictionAttributeNames = restrictions;
			//naSolverSettings.RestrictUTurns = esriNetworkForwardStarBacktrack.esriNFSBNoBacktrack;
		}

        /// <summary>
        /// When the solver has been update, the context must be updated as well
        /// </summary>
        private void UpdateContextAfterChangingSettings()
		{
			IDatasetComponent datasetComponent = m_NAContext.NetworkDataset as IDatasetComponent;
			IDENetworkDataset deNetworkDataset = datasetComponent.DataElement as IDENetworkDataset;
			m_NAContext.Solver.UpdateContext(m_NAContext, deNetworkDataset, new GPMessagesClass());
		}

        /// <summary>
        /// Prepare the text string for breaks
        /// </summary>
        private IDoubleArray ParseBreaks(string p)
		{
			String[] breaks = p.Split(' ');
			IDoubleArray pBrks = new DoubleArrayClass();
			int firstIndex = breaks.GetLowerBound(0);
			int lastIndex = breaks.GetUpperBound(0);
			for (int splitIndex = firstIndex; splitIndex <= lastIndex; splitIndex++)
			{
				try
				{
					pBrks.Add(Convert.ToDouble(breaks[splitIndex]));
				}
				catch (FormatException)
				{
					MessageBox.Show("Breaks are not properly formatted.  Use only digits separated by spaces");
					pBrks.RemoveAll();
					return pBrks;
				}
			}

			return pBrks;
		}

		#endregion

		#region Post-Solve

        /// <summary>
        /// Display analysis results in the list box
        /// </summary>
        private void DisplayOutput()
		{
			ITable table = m_NAContext.NAClasses.get_ItemByName("SAPolygons") as ITable;
			if (table.RowCount(null) > 0)
			{
				IGPMessage gpMessage = new GPMessageClass();
				lstOutput.Items.Add("FacilityID, FromBreak, ToBreak");
				ICursor cursor = table.Search(null, true);
				IRow row = cursor.NextRow();
				while (row != null)
				{
					int facilityID = (int)row.get_Value(table.FindField("FacilityID"));
					double fromBreak = (double)row.get_Value(table.FindField("FromBreak"));
					double toBreak = (double)row.get_Value(table.FindField("ToBreak"));
					lstOutput.Items.Add(facilityID.ToString() + ", " + fromBreak.ToString("#####0.00") + ", " + toBreak.ToString("#####0.00"));
					row = cursor.NextRow();
				}
			}
		}

        /// <summary>
        /// Refresh the map display
        /// <summary>
        public void RefreshMapDisplay()
        {
			IGeoDataset geoDataset = m_NAContext.NAClasses.get_ItemByName("SAPolygons") as IGeoDataset;
			IEnvelope envelope = geoDataset.Extent;
			if (!envelope.IsEmpty)
			{
				envelope.Expand(1.1, 1.1, true);
				axMapControl.Extent = envelope;

				// Call this to update the renderer for the service area polygons
				// based on the new breaks.
				m_NAContext.Solver.UpdateLayer(axMapControl.get_Layer(0) as INALayer);
			}
			axMapControl.Refresh();
		}
        /// <summary>
        /// Gather the error/warning/informative messages from GPMessages
        /// <summary>
        /// <param name="gpMessages">GPMessages container</param>
        /// <returns>string of all GPMessages</returns>
        public string GetGPMessagesAsString(IGPMessages gpMessages)
        {
            // Gather Error/Warning/Informative Messages
            var messages = new StringBuilder();
            if (gpMessages != null)
            {
                for (int i = 0; i < gpMessages.Count; i++)
                {
                    IGPMessage gpMessage = gpMessages.GetMessage(i);
                    string message = gpMessage.Description;
                    switch (gpMessages.GetMessage(i).Type)
                    {
                        case esriGPMessageType.esriGPMessageTypeError:
                            messages.AppendLine("Error " + gpMessage.ErrorCode + ": " + message);
                            break;
                        case esriGPMessageType.esriGPMessageTypeWarning:
                            messages.AppendLine("Warning: " + message);
                            break;
                        default:
                            messages.AppendLine("Information: " + message);
                            break;
                    }
                }
            }
            return messages.ToString();
        }

		#endregion
	}
}
[Visual Basic .NET]

frmServiceAreaSolver.vb

Imports Microsoft.VisualBasic
Imports System
Imports System.Windows.Forms
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.NetworkAnalyst
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geometry

Namespace ServiceAreaSolver
	Partial Public Class frmServiceAreaSolver
		Inherits Form
		Private m_naContext As INAContext

#Region "Main Form Constructor and Setup"

		Public Sub New()
			InitializeComponent()

			txtCutOff.Text = "5"
			lbOutput.Items.Clear()
			cbCostAttribute.Items.Clear()
			ckbUseRestriction.Checked = False
			axMapControl.ClearLayers()

			txtWorkspacePath.Text = Application.StartupPath & "\..\..\..\..\..\Data\SanFrancisco\SanFrancisco.gdb"
			txtNetworkDataset.Text = "Streets_ND"
			txtFeatureDataset.Text = "Transportation"
			txtInputFacilities.Text = "Hospitals"
			gbServiceAreaSolver.Enabled = False
		End Sub

#End Region

#Region "Button Clicks"

		Private Sub btnSolve_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSolve.Click
			Me.Cursor = Cursors.WaitCursor
			lbOutput.Items.Clear()

            Dim gpMessages As IGPMessages = New GPMessagesClass()
            Try
                ConfigureSolverSettings()

                If (Not m_naContext.Solver.Solve(m_naContext, gpMessages, Nothing)) Then
                    lbOutput.Items.Add("Partial Result")
                End If

                DisplayOutput()

            Catch ex As Exception
                lbOutput.Items.Add("Solve Failed: " & ex.Message)
            End Try

            lbOutput.Items.Add(GetGPMessagesAsString(GPMessages))
            UpdateMapDisplayAfterSolve()

            Me.Cursor = Cursors.Default
        End Sub

        '*********************************************************************************
        ' Gather the error/warning/informative messages from GPMessages
        '*********************************************************************************
        Public Function GetGPMessagesAsString(ByVal gpMessages As IGPMessages) As String

            Dim messages As System.Text.StringBuilder = New System.Text.StringBuilder()
            If Not gpMessages Is Nothing Then
                Dim i As Integer
                For i = 0 To gpMessages.Count - 1
                    Dim gpMessage As IGPMessage = gpMessages.GetMessage(i)
                    Dim message As String = gpMessage.Description
                    Select Case gpMessage.Type
                        Case esriGPMessageType.esriGPMessageTypeError
                            messages.AppendLine("Error " + gpMessage.ErrorCode.ToString + ": " + message)
                        Case esriGPMessageType.esriGPMessageTypeWarning
                            messages.AppendLine("Warning: " + message)
                        Case Else
                            messages.AppendLine("Information: " + message)
                    End Select
                Next
            End If

            Return messages.ToString()
        End Function

        Private Sub btnLoadMap_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnLoadMap.Click
            Me.Cursor = Cursors.WaitCursor

            gbServiceAreaSolver.Enabled = False
            lbOutput.Items.Clear()

            ' Open geodatabase and network dataset
            Dim featureWorkspace As IFeatureWorkspace = Nothing
            Dim networkDataset As INetworkDataset = Nothing

            Try
                Dim workspace As IWorkspace = OpenWorkspace(Application.StartupPath & "\..\..\..\..\..\Data\SanFrancisco\SanFrancisco.gdb")
                networkDataset = OpenNetworkDataset(workspace, "Transportation", "Streets_ND")
                featureWorkspace = TryCast(workspace, IFeatureWorkspace)
            Catch ex As Exception
                Windows.Forms.MessageBox.Show("Unable to open dataset. Error Message: " + ex.Message)
                Me.Cursor = Cursors.Default
                Return
            End Try

            CreateContextAndSolver(networkDataset)
            If m_naContext Is Nothing Then
                Me.Cursor = Cursors.Default
                Return
            End If

            LoadCostAttributes(networkDataset)
            If (Not LoadLocations(featureWorkspace)) Then
                Me.Cursor = Cursors.Default
                Return
            End If

            AddNetworkDatasetLayerToMap(networkDataset)
            AddNetworkAnalysisLayerToMap()

            ' work around a transparency issue
            Dim geoDataset As IGeoDataset = TryCast(networkDataset, IGeoDataset)
            axMapControl.Extent = axMapControl.FullExtent
            axMapControl.Extent = geoDataset.Extent

            If m_naContext IsNot Nothing Then
                gbServiceAreaSolver.Enabled = True
            End If

            Me.Cursor = Cursors.Default
        End Sub

#End Region

#Region "Set up Context and Solver"

        '*********************************************************************************
        ' Geodatabase functions
        '*********************************************************************************
        Public Function OpenWorkspace(ByVal strGDBName As String) As IWorkspace
            ' As Workspace Factories are Singleton objects, they must be instantiated with the Activator
            Dim workspaceFactory As IWorkspaceFactory = TryCast(Activator.CreateInstance(Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory")), IWorkspaceFactory)
            Return workspaceFactory.OpenFromFile(strGDBName, 0)
        End Function

        '*********************************************************************************
        ' Open the network dataset
        '*********************************************************************************
        Public Function OpenNetworkDataset(ByVal workspace As IWorkspace, ByVal featureDatasetName As String, ByVal strNDSName As String) As INetworkDataset
            ' Obtain the dataset container from the workspace
            Dim featureWorkspace As IFeatureWorkspace = TryCast(workspace, IFeatureWorkspace)
            Dim featureDataset As IFeatureDataset = featureWorkspace.OpenFeatureDataset(featureDatasetName)
            Dim featureDatasetExtensionContainer As IFeatureDatasetExtensionContainer = TryCast(featureDataset, IFeatureDatasetExtensionContainer)
            Dim featureDatasetExtension As IFeatureDatasetExtension = featureDatasetExtensionContainer.FindExtension(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset)
            Dim datasetContainer3 As IDatasetContainer3 = TryCast(featureDatasetExtension, IDatasetContainer3)

            ' Use the container to open the network dataset
            Dim dataset As Object = datasetContainer3.DatasetByName(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset, strNDSName)
            Return TryCast(dataset, INetworkDataset)
        End Function

        Private Sub CreateContextAndSolver(ByVal networkDataset As INetworkDataset)
            If networkDataset Is Nothing Then
                Return
            End If

            Dim datasetComponent As IDatasetComponent = TryCast(networkDataset, IDatasetComponent)
            Dim deNetworkDataset As IDENetworkDataset = TryCast(datasetComponent.DataElement, IDENetworkDataset)

            Dim naSolver As INASolver = New NAServiceAreaSolverClass()
            m_naContext = naSolver.CreateContext(deNetworkDataset, "ServiceArea")
            Dim naContextEdit As INAContextEdit = TryCast(m_naContext, INAContextEdit)
            naContextEdit.Bind(networkDataset, New GPMessagesClass())
        End Sub

#End Region

#Region "Load Form Controls"

        Private Sub LoadCostAttributes(ByVal networkDataset As INetworkDataset)
            cbCostAttribute.Items.Clear()

            Dim attrCount As Integer = networkDataset.AttributeCount
            For attrIndex As Integer = 0 To attrCount - 1
                Dim networkAttribute As INetworkAttribute = networkDataset.Attribute(attrIndex)
                If networkAttribute.UsageType = esriNetworkAttributeUsageType.esriNAUTCost Then
                    cbCostAttribute.Items.Add(networkAttribute.Name)
                End If
            Next attrIndex

            If cbCostAttribute.Items.Count > 0 Then
                cbCostAttribute.SelectedIndex = 0
            End If
        End Sub

        Private Function LoadLocations(ByVal featureWorkspace As IFeatureWorkspace) As Boolean
            Dim inputFeatureClass As IFeatureClass = Nothing
            Try
                inputFeatureClass = featureWorkspace.OpenFeatureClass(txtInputFacilities.Text)
            Catch e1 As Exception
                MessageBox.Show("Specified input feature class does not exist")
                Return False
            End Try

            Dim classes As INamedSet = m_naContext.NAClasses
            Dim naClass As INAClass = TryCast(classes.ItemByName("Facilities"), INAClass)

            ' delete existing locations, except barriers
            naClass.DeleteAllRows()

            ' Create a NAClassLoader and set the snap tolerance (meters unit)
            Dim naClassLoader As INAClassLoader = New NAClassLoaderClass()
            naClassLoader.Locator = m_naContext.Locator
            naClassLoader.Locator.SnapTolerance = 100
            naClassLoader.NAClass = naClass

            ' Create field map to automatically map fields from input class to NAClass
            Dim naClassFieldMap As INAClassFieldMap = New NAClassFieldMapClass()
            naClassFieldMap.CreateMapping(naClass.ClassDefinition, inputFeatureClass.Fields)
            naClassLoader.FieldMap = naClassFieldMap

            ' Avoid loading network locations onto non-traversable portions of elements
            Dim locator As INALocator3 = TryCast(m_naContext.Locator, INALocator3)
            locator.ExcludeRestrictedElements = True
            locator.CacheRestrictedElements(m_naContext)

            ' load network locations
            Dim rowsIn As Integer = 0
            Dim rowsLocated As Integer = 0
            naClassLoader.Load(TryCast(inputFeatureClass.Search(Nothing, True), ICursor), Nothing, rowsIn, rowsLocated)

            If rowsLocated <= 0 Then
                MessageBox.Show("Facilities were not loaded from input feature class")
                Return False
            End If

            ' Message all of the network analysis agents that the analysis context has changed
            Dim naContextEdit As INAContextEdit = TryCast(m_naContext, INAContextEdit)
            naContextEdit.ContextChanged()

            Return True
        End Function

        Private Sub AddNetworkAnalysisLayerToMap()
            Dim layer As ILayer = TryCast(m_naContext.Solver.CreateLayer(m_naContext), ILayer)
            layer.Name = m_naContext.Solver.DisplayName
            axMapControl.AddLayer(layer)
        End Sub

        Private Sub AddNetworkDatasetLayerToMap(ByVal networkDataset As INetworkDataset)
            Dim networkLayer As INetworkLayer = New NetworkLayerClass()
            networkLayer.NetworkDataset = networkDataset
            Dim layer As ILayer = TryCast(networkLayer, ILayer)
            layer.Name = "Network Dataset"
            axMapControl.AddLayer(layer)
        End Sub

#End Region

#Region "Solver Settings"

        Private Sub ConfigureSolverSettings()
            ConfigureSettingsSpecificToServiceAreaSolver()

            ConfigureGenericSolverSettings()

            UpdateContextAfterChangingSettings()
        End Sub

        Private Sub ConfigureSettingsSpecificToServiceAreaSolver()
            Dim naSASolver As INAServiceAreaSolver = TryCast(m_naContext.Solver, INAServiceAreaSolver)

            naSASolver.DefaultBreaks = ParseBreaks(txtCutOff.Text)

            naSASolver.MergeSimilarPolygonRanges = False
            naSASolver.OutputPolygons = esriNAOutputPolygonType.esriNAOutputPolygonSimplified
            naSASolver.OverlapLines = True
            naSASolver.SplitLinesAtBreaks = False
            naSASolver.TravelDirection = esriNATravelDirection.esriNATravelDirectionFromFacility
            naSASolver.OutputLines = esriNAOutputLineType.esriNAOutputLineNone
        End Sub

        Private Sub ConfigureGenericSolverSettings()
            Dim naSolverSettings As INASolverSettings = TryCast(m_naContext.Solver, INASolverSettings)
            naSolverSettings.ImpedanceAttributeName = cbCostAttribute.Text

            ' set the oneway restriction, if necessary
            Dim restrictions As IStringArray = naSolverSettings.RestrictionAttributeNames
            restrictions.RemoveAll()
            If ckbUseRestriction.Checked Then
                restrictions.Add("oneway")
            End If
            naSolverSettings.RestrictionAttributeNames = restrictions
            naSolverSettings.RestrictUTurns = esriNetworkForwardStarBacktrack.esriNFSBNoBacktrack
        End Sub

        Private Sub UpdateContextAfterChangingSettings()
            Dim datasetComponent As IDatasetComponent = TryCast(m_naContext.NetworkDataset, IDatasetComponent)
            Dim deNetworkDataset As IDENetworkDataset = TryCast(datasetComponent.DataElement, IDENetworkDataset)
            m_naContext.Solver.UpdateContext(m_naContext, deNetworkDataset, New GPMessagesClass())
        End Sub

        Private Function ParseBreaks(ByVal p As String) As IDoubleArray
            Dim breaks() As String = p.Split(" "c)
            Dim pBrks As IDoubleArray = New DoubleArrayClass()
            Dim firstIndex As Integer = breaks.GetLowerBound(0)
            Dim lastIndex As Integer = breaks.GetUpperBound(0)
            For splitIndex As Integer = firstIndex To lastIndex
                Try
                    pBrks.Add(Convert.ToDouble(breaks(splitIndex)))
                Catch e1 As FormatException
                    MessageBox.Show("Breaks are not properly formatted.  Use only digits separated by spaces")
                    pBrks.RemoveAll()
                    Return pBrks
                End Try
            Next splitIndex

            Return pBrks
        End Function

#End Region

#Region "Post-Solve"

        Private Sub LoadListboxAfterPartialSolve(ByVal gpMessages As IGPMessages)
            lbOutput.Items.Add("Partial Solve Generated.")
            For msgIndex As Integer = 0 To gpMessages.Messages.Count - 1
                Dim errorText As String = ""
                Select Case gpMessages.GetMessage(msgIndex).Type
                    Case esriGPMessageType.esriGPMessageTypeError
                        errorText = "Error " & gpMessages.GetMessage(msgIndex).ErrorCode.ToString() & " " & gpMessages.GetMessage(msgIndex).Description
                    Case esriGPMessageType.esriGPMessageTypeWarning
                        errorText = "Warning " & gpMessages.GetMessage(msgIndex).ErrorCode.ToString() & " " & gpMessages.GetMessage(msgIndex).Description
                    Case Else
                        errorText = "Information " & gpMessages.GetMessage(msgIndex).Description
                End Select
                lbOutput.Items.Add(errorText)
            Next msgIndex
        End Sub

        Private Sub DisplayOutput()
            Dim table As ITable = TryCast(m_naContext.NAClasses.ItemByName("SAPolygons"), ITable)
            If table.RowCount(Nothing) > 0 Then
                Dim gpMessage As IGPMessage = New GPMessageClass()
                lbOutput.Items.Add("FacilityID, FromBreak, ToBreak")
                Dim cursor As ICursor = table.Search(Nothing, True)
                Dim row As IRow = cursor.NextRow()
                Do While row IsNot Nothing
                    Dim facilityID As Integer = CInt(Fix(row.Value(table.FindField("FacilityID"))))
                    Dim fromBreak As Double = CDbl(row.Value(table.FindField("FromBreak")))
                    Dim toBreak As Double = CDbl(row.Value(table.FindField("ToBreak")))
                    lbOutput.Items.Add(facilityID.ToString() & ", " & fromBreak.ToString("#####0.00") & ", " & toBreak.ToString("#####0.00"))
                    row = cursor.NextRow()
                Loop
            End If
        End Sub

        Private Sub UpdateMapDisplayAfterSolve()
            ' Zoom to the extent of the service areas
            Dim geoDataset As IGeoDataset = TryCast(m_naContext.NAClasses.ItemByName("SAPolygons"), IGeoDataset)
            Dim envelope As IEnvelope = geoDataset.Extent
            If (Not envelope.IsEmpty) Then
                envelope.Expand(1.1, 1.1, True)
                axMapControl.Extent = envelope

                ' Call this to update the renderer for the service area polygons
                ' based on the new breaks.
                m_naContext.Solver.UpdateLayer(TryCast(axMapControl.get_Layer(0), INALayer))
            End If
            axMapControl.Refresh()
        End Sub

#End Region
	End Class
End Namespace