ArcObjects Library Reference  

frmLocationAllocationSolver

About the Location-allocation solver Sample

[C#]

frmLocationAllocationSolver.cs

//*************************************************************************************
//       ArcGIS Network Analyst extension - Location-Allocation Demonstration
//
//   This simple code shows how to :
//    1) Open a workspace and open a Network Dataset
//    2) Create a NAContext and its NASolver
//    3) Load Facilites/DemandPoints from Feature Classes and create Network Locations
//    4) Set the Solver parameters
//    5) Solve a Location-Allocation problem
//    6) Read the Facilities and LALines output to display the facilities chosen
//       and the list the demand points allocated
//************************************************************************************

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

namespace LocationAllocationSolver
{
	public partial class frmLocationAllocationSolver : Form
	{
		private INAContext m_NAContext;
		private string m_ProblemType = "Minimize Impedance";

		public frmLocationAllocationSolver()
		{
			InitializeComponent();
			Initialize();
		}

		//*********************************************************************************
		// Initialize the form, create a NA context, load some locations and draw the map
		//*********************************************************************************
		private void Initialize()
		{
			// Open Geodatabase and network dataset
			IFeatureWorkspace featureWorkspace = OpenWorkspace(Application.StartupPath + @"\..\..\..\..\..\Data\SanFrancisco\SanFrancisco.gdb") as IFeatureWorkspace;
			INetworkDataset networkDataset = OpenNetworkDataset(featureWorkspace as IWorkspace, "Transportation", "Streets_ND");

			// Create NAContext and NASolver
			m_NAContext = CreateSolverContext(networkDataset);

			// Get Cost Attributes and populate the combo drop down box
			INetworkAttribute networkAttribute;
			for (int i = 0; i < networkDataset.AttributeCount - 1; i++)
			{
				networkAttribute = networkDataset.get_Attribute(i);
				if (networkAttribute.UsageType == esriNetworkAttributeUsageType.esriNAUTCost)
				{
					cboCostAttribute.Items.Add(networkAttribute.Name);
					cboCostAttribute.SelectedIndex = 0;
				}
			}

			// Set the default number of facilities to solve for
			txtFacilitiesToLocate.Text = "1";

			// Set up the no cutoff for the Minimize Impedance case.
			// See the cboProblemType_SelectedIndexChanged routine for how this is managed for other problem types
			txtCutOff.Text = "<None>";

			// Populate combo box with Location-Allocation problem types
			cboProblemType.Items.Add("Minimize Impedance");
			cboProblemType.Items.Add("Maximize Coverage");
            cboProblemType.Items.Add("Maximize Capacitated Coverage");
            cboProblemType.Items.Add("Minimize Facilities");
			cboProblemType.Items.Add("Maximize Attendance");
			cboProblemType.Items.Add("Maximize Market Share");
			cboProblemType.Items.Add("Target Market Share");
			cboProblemType.Text = "Minimize Impedance";
			m_ProblemType = "Minimize Impedance";

			// Populate combo box with Impedance Transformation choices
			cboImpTransformation.Items.Add("Linear");
			cboImpTransformation.Items.Add("Power");
			cboImpTransformation.Items.Add("Exponential");
			cboImpTransformation.Text = "Linear";

			// Set the default impedance transformation parameter
			txtImpParameter.Text = "1.0";

			// Set up the default percentage for the Target Market Share problem type
			txtTargetMarketShare.Text = "10.0";

            // Set up the default capacity
            txtDefaultCapacity.Text = "1.0";

			// Load facility locations from feature class
			IFeatureClass inputFClass = featureWorkspace.OpenFeatureClass("CandidateStores");
			LoadNANetworkLocations("Facilities", inputFClass, 500);

			// Load demand point locations from feature class
			inputFClass = featureWorkspace.OpenFeatureClass("TractCentroids");
			LoadNANetworkLocations("DemandPoints", inputFClass, 500);

			// Create Layer for Network Dataset and add to Ax Map Control
			ILayer layer;
			INetworkLayer networkLayer;
			networkLayer = new NetworkLayerClass();
			networkLayer.NetworkDataset = networkDataset;
			layer = networkLayer as ILayer;
			layer.Name = "Network Dataset";
			axMapControl.AddLayer(layer, 0);

			// Create a Network Analysis Layer and add to Ax Map Control
			INALayer naLayer = m_NAContext.Solver.CreateLayer(m_NAContext);
			layer = naLayer as ILayer;
			layer.Name = m_NAContext.Solver.DisplayName;
			axMapControl.AddLayer(layer, 0);
		}

		//*********************************************************************************
		// ArcGIS Network Analyst extension functions
		// ********************************************************************************

		//*********************************************************************************
		// Create NASolver and NAContext
		//*********************************************************************************
		public INAContext CreateSolverContext(INetworkDataset networkDataset)
		{
			//Get the Data Element
			IDENetworkDataset deNDS = GetDENetworkDataset(networkDataset);

			INASolver naSolver = new NALocationAllocationSolverClass();
			INAContextEdit contextEdit = naSolver.CreateContext(deNDS, naSolver.Name) as INAContextEdit;
			contextEdit.Bind(networkDataset, new GPMessagesClass());
			return contextEdit as INAContext;
		}

		//*********************************************************************************
		// Load Network Locations
		//*********************************************************************************
		public void LoadNANetworkLocations(string strNAClassName, IFeatureClass inputFC, double maxSnapTolerance)
		{
			INamedSet classes = m_NAContext.NAClasses;
			INAClass naClass = classes.get_ItemByName(strNAClassName) as INAClass;

			// Delete existing Locations before loading new ones
			naClass.DeleteAllRows();

			// Create a NAClassLoader and set the snap tolerance (meters unit)
			INAClassLoader classLoader = new NAClassLoader();
			classLoader.Locator = m_NAContext.Locator;
            if (maxSnapTolerance > 0) ((INALocator3)classLoader.Locator).MaxSnapTolerance = maxSnapTolerance;
            classLoader.NAClass = naClass;

			//Create field map to automatically map fields from input class to NAClass
			INAClassFieldMap fieldMap = new NAClassFieldMap();
			fieldMap.CreateMapping(naClass.ClassDefinition, inputFC.Fields);
			classLoader.FieldMap = fieldMap;

			// 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;
			IFeatureCursor featureCursor = inputFC.Search(null, true);
			classLoader.Load((ICursor)featureCursor, null, ref rowsIn, ref rowsLocated);

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

		//*********************************************************************************
		// Set Solver Settings for the Location-Allocation Solver
		//*********************************************************************************
		public void SetSolverSettings()
		{
			//Set Location-Allocation specific Settings
			INASolver naSolver = m_NAContext.Solver;

			INALocationAllocationSolver2 laSolver = naSolver as INALocationAllocationSolver2;

			// Set number of facilities to locate
			if (txtFacilitiesToLocate.Text.Length > 0 && IsNumeric(txtFacilitiesToLocate.Text))
				laSolver.NumberFacilitiesToLocate = int.Parse(txtFacilitiesToLocate.Text);
			else
				laSolver.NumberFacilitiesToLocate = 1;

			// Set impedance cutoff
			if (txtCutOff.Text.Length > 0 && IsNumeric(txtCutOff.Text.Trim()))
				laSolver.DefaultCutoff = txtCutOff.Text;
			else
				laSolver.DefaultCutoff = null;

			// Set up Location-Allocation problem type
			if (cboProblemType.Text.Equals("Maximize Attendance"))
				laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeAttendance;
			else if (cboProblemType.Text.Equals("Maximize Coverage"))
				laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeCoverage;
            else if (cboProblemType.Text.Equals("Maximize Capacitated Coverage"))
                laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeCapacitatedCoverage;
            else if (cboProblemType.Text.Equals("Minimize Facilities"))
				laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeCoverageMinimizeFacilities;
			else if (cboProblemType.Text.Equals("Maximize Market Share"))
				laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeMarketShare;
			else if (cboProblemType.Text.Equals("Minimize Impedance"))
				laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMinimizeWeightedImpedance;
			else if (cboProblemType.Text.Equals("Target Market Share"))
				laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTTargetMarketShare;
			else
				laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMinimizeWeightedImpedance;

			// Set Impedance Transformation type
			if (cboImpTransformation.Text.Equals("Linear"))
				laSolver.ImpedanceTransformation = esriNAImpedanceTransformationType.esriNAITTLinear;
			else if (cboImpTransformation.Text.Equals("Power"))
				laSolver.ImpedanceTransformation = esriNAImpedanceTransformationType.esriNAITTPower;
			else if (cboImpTransformation.Text.Equals("Exponential"))
				laSolver.ImpedanceTransformation = esriNAImpedanceTransformationType.esriNAITTExponential;

			// Set Impedance Transformation Parameter (distance decay beta)
			if (txtImpParameter.Text.Length > 0 && IsNumeric(txtCutOff.Text.Trim()))
				laSolver.TransformationParameter = double.Parse(txtImpParameter.Text);
			else
				laSolver.TransformationParameter = 1.0;

			// Set target market share percentage (should be between 0.0 and 100.0)
			if (txtTargetMarketShare.Text.Length > 0 && IsNumeric(txtCutOff.Text.Trim()))
			{
				double targetPercentage;
				targetPercentage = double.Parse(txtTargetMarketShare.Text);

				if ((targetPercentage <= 0.0) || (targetPercentage > 100.0))
				{
					targetPercentage = 10.0;
					lstOutput.Items.Add("Target percentage out of range. Reset to 10%");
				}
				laSolver.TargetMarketSharePercentage = targetPercentage;
				txtTargetMarketShare.Text = laSolver.TargetMarketSharePercentage.ToString();
			}
			else
				laSolver.TargetMarketSharePercentage = 10.0;

            // Set default capacity
            if (txtDefaultCapacity.Text.Length > 0 && IsNumeric(txtDefaultCapacity.Text.Trim()))
            {
                double defaultCapacity;
                defaultCapacity = double.Parse(txtDefaultCapacity.Text);

                if ((defaultCapacity <= 0.0))
                {
                    defaultCapacity = 1.0;
                    lstOutput.Items.Add("Default capacity must be greater than zero.");
                }
                laSolver.DefaultCapacity = defaultCapacity;
                txtDefaultCapacity.Text = laSolver.DefaultCapacity.ToString();
            }
            else
                laSolver.DefaultCapacity = 1.0;


			// Set any other solver settings
			laSolver.OutputLines = esriNAOutputLineType.esriNAOutputLineStraight;
			laSolver.TravelDirection = esriNATravelDirection.esriNATravelDirectionFromFacility;

			// Set the impedance attribute
			INASolverSettings naSolverSettings = naSolver as INASolverSettings;

			naSolverSettings.ImpedanceAttributeName = cboCostAttribute.Text;

			naSolverSettings.IgnoreInvalidLocations = true;

			// Do not forget to update the context after you set your impedance
			naSolver.UpdateContext(m_NAContext, GetDENetworkDataset(m_NAContext.NetworkDataset), new GPMessagesClass());
		}

		//*********************************************************************************
		//Get Located Facilities information from the Facilities Class and summarize some statistics
		//*********************************************************************************
		public void GetLAFacilitiesOutput(string strNAClass)
		{
			ITable table = m_NAContext.NAClasses.get_ItemByName(strNAClass) as ITable;
			if (table == null)
				lstOutput.Items.Add("Impossible to get the " + strNAClass + " table");

			if (table.RowCount(null) > 0)
			{
				ICursor cursor;
				IRow row;
				string facilityName;
				double demandWeight, total_impedance;
				long demandCount;
				long facilityCount = 0;
				long sumDemand = 0;
				double sumWeightedDistance = 0.0;

				cursor = table.Search(null, false);
				row = cursor.NextRow();
				while (row != null)
				{
					demandCount = long.Parse(row.get_Value(table.FindField("DemandCount")).ToString());
					if (demandCount > 0)
					{
						facilityCount = facilityCount + 1;
						facilityName = row.get_Value(table.FindField("Name")).ToString();
						demandWeight = double.Parse(row.get_Value(table.FindField("DemandWeight")).ToString());
						total_impedance = double.Parse(row.get_Value(table.FindField("TotalWeighted_" + cboCostAttribute.Text)).ToString());
						sumWeightedDistance = sumWeightedDistance + total_impedance;
						sumDemand = sumDemand + demandCount;
					}
					row = cursor.NextRow();
				}
				lstOutput.Items.Add("Number of facilities Located " + facilityCount.ToString());
				lstOutput.Items.Add("Number of demand points Allocated " + sumDemand.ToString());
				lstOutput.Items.Add("Sum of weighted " + cboCostAttribute.Text + " " + sumWeightedDistance.ToString());
				lstOutput.Items.Add("");
			}
			lstOutput.Refresh();
		}

		//*********************************************************************************
		// Get the Impedance Cost form the LALines Class Output and list out the allocation
		//*********************************************************************************
		public void GetLALinesOutput(string strNAClass)
		{
			ITable table = m_NAContext.NAClasses.get_ItemByName(strNAClass) as ITable;
			if (table == null)
			{
				lstOutput.Items.Add("Impossible to get the " + strNAClass + " table");
			}
			lstOutput.Items.Add("Allocation Table:");
			if (table.RowCount(null) > 0)
			{
				lstOutput.Items.Add("DemandID,FacilityID,Weight,TotalWeighted_" + cboCostAttribute.Text);
				double total_impedance;
				long demandID;
				long facilityID;
				double facilityWeight;
				ICursor cursor;
				IRow row;

				cursor = table.Search(null, false);
				row = cursor.NextRow();
				while (row != null)
				{
					facilityID = long.Parse(row.get_Value(table.FindField("FacilityID")).ToString());
					demandID = long.Parse(row.get_Value(table.FindField("DemandID")).ToString());
					facilityWeight = double.Parse(row.get_Value(table.FindField("Weight")).ToString());
					total_impedance = double.Parse(row.get_Value(table.FindField("TotalWeighted_" + cboCostAttribute.Text)).ToString());
					lstOutput.Items.Add(demandID.ToString() + ",\t" + facilityID.ToString() +
					  ",\t" + facilityWeight.ToString() + ",\t" + total_impedance.ToString("F2"));
					row = cursor.NextRow();
				}
			}
			lstOutput.Refresh();
		}

		//*********************************************************************************
		// Geodatabase functions
		// ********************************************************************************
		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;
			return workspaceFactory.OpenFromFile(strGDBName, 0);
		}

		//*********************************************************************************
		// Open the network dataset
		//*********************************************************************************
		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;
		}

		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;
		}

		private bool IsNumeric(string str)
		{
			try
			{
				double.Parse(str.Trim());
			}
			catch (Exception) { return false; }
			return true;
		}

		//*********************************************************************************
		// Read the solver settings from the user and solve the Location-Allocation problem
		//*********************************************************************************
		private void cmdSolve_Click(object sender, EventArgs e)
		{
            IGPMessages gpMessages = new GPMessagesClass();
            try
			{
				lstOutput.Items.Clear();
				lstOutput.Items.Add("Solving...");

				SetSolverSettings();

				// Solve
				if (!m_NAContext.Solver.Solve(m_NAContext, gpMessages, null))
				{
					GetLAFacilitiesOutput("Facilities");
					GetLALinesOutput("LALines");
				}
				else
					lstOutput.Items.Add("Partial Result");

				//Zoom to the extent of the route
				IGeoDataset geoDataset = m_NAContext.NAClasses.get_ItemByName("LALines") as IGeoDataset;
				IEnvelope envelope = geoDataset.Extent;
				if (!envelope.IsEmpty)
					envelope.Expand(1.1, 1.1, true);
				axMapControl.Extent = envelope;
				axMapControl.Refresh();
			}

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

            lstOutput.Items.Add(GetGPMessagesAsString(gpMessages));
            cmdSolve.Text = "Solve";
        }

        //*********************************************************************************
        // Gather the error/warning/informative messages from GPMessages
        //*********************************************************************************
        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();
        }

		//*********************************************************************************
		// Manage the cutoff, either <None> or some intelligent default
		//*********************************************************************************
		private void cboProblemType_SelectedIndexChanged(object sender, EventArgs e)
		{
			// All problem types except Minimize Impedance need an impedance cutoff.
			// So manage an intelligent default other than <None> for them.
			// If cutoff is set and problem type switches back to Minimize Impedance, reset cutoff to <None>
			// Note: 3.0 is ok for Minutes but if impedance is something else like Meters, 3.0 will be too small
			// and cause some solver errors like "Value does not fall within the expected range."
			if (cboProblemType.Text.Equals("Minimize Impedance"))
			{
				if (!m_ProblemType.Equals("Minimize Impedance"))
					if (!txtCutOff.Text.Equals("<None>"))
						txtCutOff.Text = "<None>";
			}
			else
				if (txtCutOff.Text.Equals("<None>"))
					txtCutOff.Text = "3.0";

			m_ProblemType = cboProblemType.Text;
		}
	}
}

[Visual Basic .NET]

frmLocationAllocationSolver.vb

'*************************************************************************************
'       ArcGIS Network Analyst extension - Location-Allocation Demonstration
'
'   This simple code shows how to :
'    1) Open a workspace and open a Network DataSet
'    2) Create a NAContext and its NASolver
'    3) Load Incidents/Facilites from Feature Classes and create Network Locations
'    4) Set the Solver parameters
'    5) Solve a Location-Allocation problem
'    6) Read the Facilities and LALines output to display the facilities chosen
'       and the list the demand points allocated
'************************************************************************************

Imports System
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.NetworkAnalyst
Imports Microsoft.VisualBasic

Public Class frmLocationAllocationSolver
    Private m_NAContext As INAContext
    Private m_ProblemType As String = "Minimize Impedance"

    Public Sub New()
        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
        Initialize()
    End Sub

    '*********************************************************************************
    ' Initialize the form, create a NA context, load some locations and draw the map
    '*********************************************************************************
    Private Sub Initialize()
        ' Open geodatabase and network dataset
        Dim featureWorkspace As IFeatureWorkspace = Nothing
        Dim networkDataset As INetworkDataset = Nothing

        Try
            ' Open the geodatabase and network dataset
            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.Close()
            Return
        End Try

        ' Create NAContext and NASolver
        m_NAContext = CreateSolverContext(networkDataset)

        ' Get Cost Attributes and populate the combo drop down box
        Dim networkAttribute As INetworkAttribute
        For i As Integer = 0 To networkDataset.AttributeCount - 2
            networkAttribute = networkDataset.Attribute(i)
            If networkAttribute.UsageType = esriNetworkAttributeUsageType.esriNAUTCost Then
                cboCostAttribute.Items.Add(networkAttribute.Name)
                cboCostAttribute.SelectedIndex = 0
            End If
        Next i

        ' Set the default number of facilities to solve for
        txtFacilitiesToLocate.Text = "1"

        ' Set up the no cutoff for the Minimize Impedance case.
        ' See the cboProblemType_SelectedIndexChanged routine for how this is managed for other problem types
        txtCutOff.Text = "<None>"

        ' Load facility locations from FC
        Dim inputFClass As IFeatureClass = featureWorkspace.OpenFeatureClass("CandidateStores")
        LoadNANetworkLocations("Facilities", inputFClass, 500)

        ' Load demand point locations from FC
        inputFClass = featureWorkspace.OpenFeatureClass("TractCentroids")
        LoadNANetworkLocations("DemandPoints", inputFClass, 500)

        ' Populate combo box with Location-Allocation problem types
        cboProblemType.Items.Add("Minimize Impedance")
        cboProblemType.Items.Add("Maximize Coverage")
        cboProblemType.Items.Add("Maximize Capacitated Coverage")
        cboProblemType.Items.Add("Minimize Facilities")
        cboProblemType.Items.Add("Maximize Attendance")
        cboProblemType.Items.Add("Maximize Market Share")
        cboProblemType.Items.Add("Target Market Share")
        cboProblemType.Text = "Minimize Impedance"
        m_ProblemType = "Minimize Impedance"

        ' Populate combo box with Impedance Transformation choices
        cboImpTransformation.Items.Add("Linear")
        cboImpTransformation.Items.Add("Power")
        cboImpTransformation.Items.Add("Exponential")
        cboImpTransformation.Text = "Linear"

        ' Set the default impedance transformation parameter
        txtImpParameter.Text = "1.0"

        ' Set up the default percentage for the Target Market Share problem type
        txtTargetMarketShare.Text = "10.0"

        ' Set up the default capacity
        txtDefaultCapacity.Text = "1.0"

        ' Create Layer for Network Dataset and add to Ax Map Control
        Dim networkLayer As INetworkLayer = New NetworkLayerClass
        networkLayer.NetworkDataset = networkDataset
        Dim layer As ILayer = TryCast(networkLayer, ILayer)
        layer.Name = "Network Dataset"
        axMapControl.AddLayer(layer, 0)

        ' Create a Network Analysis Layer and add to Ax Map Control
        Dim naLayer As INALayer = m_NAContext.Solver.CreateLayer(m_NAContext)
        layer = TryCast(naLayer, ILayer)
        layer.Name = m_NAContext.Solver.DisplayName
        axMapControl.AddLayer(layer, 0)
    End Sub

    '*********************************************************************************
    ' ArcGIS Network Analyst extension functions
    ' ********************************************************************************

    '*********************************************************************************
    ' Create NASolver and NAContext
    '*********************************************************************************
    Public Function CreateSolverContext(ByVal networkDataset As INetworkDataset) As INAContext
        'Get the Data Element
        Dim deNDS As IDENetworkDataset = GetDENetworkDataset(networkDataset)

        Dim naSolver As INASolver = New NALocationAllocationSolverClass()
        Dim contextEdit As INAContextEdit = TryCast(naSolver.CreateContext(deNDS, naSolver.Name), INAContextEdit)
        contextEdit.Bind(networkDataset, New GPMessagesClass())
        Return TryCast(contextEdit, INAContext)
    End Function

    '*********************************************************************************
    ' Load Network Locations
    '*********************************************************************************
    Public Sub LoadNANetworkLocations(ByVal strNAClassName As String, ByVal inputFC As IFeatureClass, ByVal maxSnapTolerance As Double)
        Dim classes As INamedSet = m_NAContext.NAClasses
        Dim naClass As INAClass = TryCast(classes.ItemByName(strNAClassName), INAClass)

        ' Delete existing Locations before loading new ones
        naClass.DeleteAllRows()

        ' 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)

        ' Create a NAClassLoader and set the maximum snap tolerance (meters unit)
        Dim classLoader As INAClassLoader = New NAClassLoader
        classLoader.Locator = m_NAContext.Locator
        If maxSnapTolerance > 0 Then
            locator.MaxSnapTolerance = maxSnapTolerance
        End If
        classLoader.NAClass = naClass

        'Create field map to automatically map fields from input class to naclass
        Dim fieldMap As INAClassFieldMap = New NAClassFieldMap()
        fieldMap.CreateMapping(naClass.ClassDefinition, inputFC.Fields)
        classLoader.FieldMap = fieldMap

        'Load Network Locations
        Dim rowsIn As Integer = 0
        Dim rowsLocated As Integer = 0
        Dim featureCursor As IFeatureCursor = inputFC.Search(Nothing, True)
        classLoader.Load(CType(featureCursor, ICursor), Nothing, rowsIn, rowsLocated)

        'Message all of the network analysis agents that the analysis context has changed
        CType(m_NAContext, INAContextEdit).ContextChanged()
    End Sub

    '*********************************************************************************
    ' Set Solver Settings
    '*********************************************************************************
    Public Sub SetSolverSettings()
        'Set Location-Allocation specific Settings
        Dim naSolver As INASolver = m_NAContext.Solver

        Dim laSolver As INALocationAllocationSolver2 = TryCast(naSolver, INALocationAllocationSolver2)

        ' Set number of facilities to locate
        If txtFacilitiesToLocate.Text.Length > 0 AndAlso IsNumeric(txtFacilitiesToLocate.Text) Then
            laSolver.NumberFacilitiesToLocate = Integer.Parse(txtFacilitiesToLocate.Text)
        Else
            laSolver.NumberFacilitiesToLocate = 1
        End If

        ' Set impedance cutoff
        If txtCutOff.Text.Length > 0 AndAlso IsNumeric(txtCutOff.Text.Trim()) Then
            laSolver.DefaultCutoff = txtCutOff.Text
        Else
            laSolver.DefaultCutoff = Nothing
        End If

        ' Set up Location-Allocation problem type
        If cboProblemType.Text.Equals("Maximize Attendance") Then
            laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeAttendance
        ElseIf cboProblemType.Text.Equals("Maximize Coverage") Then
            laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeCoverage
        ElseIf cboProblemType.Text.Equals("Maximize Capacitated Coverage") Then
            laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeCapacitatedCoverage
        ElseIf cboProblemType.Text.Equals("Minimize Facilities") Then
            laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeCoverageMinimizeFacilities
        ElseIf cboProblemType.Text.Equals("Maximize Market Share") Then
            laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMaximizeMarketShare
        ElseIf cboProblemType.Text.Equals("Minimize Impedance") Then
            laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMinimizeWeightedImpedance
        ElseIf cboProblemType.Text.Equals("Target Market Share") Then
            laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTTargetMarketShare
        Else
            laSolver.ProblemType = esriNALocationAllocationProblemType.esriNALAPTMinimizeWeightedImpedance
        End If

        ' Set Impedance Transformation type
        If cboImpTransformation.Text.Equals("Linear") Then
            laSolver.ImpedanceTransformation = esriNAImpedanceTransformationType.esriNAITTLinear
        ElseIf cboImpTransformation.Text.Equals("Power") Then
            laSolver.ImpedanceTransformation = esriNAImpedanceTransformationType.esriNAITTPower
        ElseIf cboImpTransformation.Text.Equals("Exponential") Then
            laSolver.ImpedanceTransformation = esriNAImpedanceTransformationType.esriNAITTExponential
        End If

        ' Set Impedance Transformation Parameter (distance decay beta)
        If txtImpParameter.Text.Length > 0 AndAlso IsNumeric(txtCutOff.Text.Trim()) Then
            laSolver.TransformationParameter = Double.Parse(txtImpParameter.Text)
        Else
            laSolver.TransformationParameter = 1.0
        End If

        ' Set target market share percentage (should be between 0.0 and 100.0)
        If txtTargetMarketShare.Text.Length > 0 AndAlso IsNumeric(txtCutOff.Text.Trim()) Then
            Dim targetPercentage As Double
            targetPercentage = Double.Parse(txtTargetMarketShare.Text)

            If (targetPercentage <= 0.0) OrElse (targetPercentage > 100.0) Then
                targetPercentage = 10.0
                lstOutput.Items.Add("Target percentage out of range. Reset to 10%")
            End If
            laSolver.TargetMarketSharePercentage = targetPercentage
            txtTargetMarketShare.Text = laSolver.TargetMarketSharePercentage.ToString()
        Else
            laSolver.TargetMarketSharePercentage = 10.0
        End If

        ' Set default capacity
        If txtDefaultCapacity.Text.Length > 0 AndAlso IsNumeric(txtDefaultCapacity.Text.Trim()) Then
            Dim defaultCapacity As Double
            defaultCapacity = Double.Parse(txtDefaultCapacity.Text)

            If (defaultCapacity <= 0.0) Then
                defaultCapacity = 1.0
                lstOutput.Items.Add("Default capacity must be greater than zero.")
            End If

            laSolver.DefaultCapacity = defaultCapacity
            txtDefaultCapacity.Text = laSolver.DefaultCapacity.ToString()
        Else
            laSolver.DefaultCapacity = 1.0
        End If

        ' Set any other solver settings
        laSolver.OutputLines = esriNAOutputLineType.esriNAOutputLineStraight
        laSolver.TravelDirection = esriNATravelDirection.esriNATravelDirectionFromFacility

        ' Set the impedance attribute
        Dim naSolverSettings As INASolverSettings = TryCast(naSolver, INASolverSettings)

        naSolverSettings.ImpedanceAttributeName = cboCostAttribute.Text

        naSolverSettings.IgnoreInvalidLocations = True

        ' Do not forget to update the context after you set your impedance
        naSolver.UpdateContext(m_NAContext, GetDENetworkDataset(m_NAContext.NetworkDataset), New GPMessagesClass())
    End Sub

    '*********************************************************************************
    'Get Located Facilities information from the Facilities Class and summarize some statistics
    '*********************************************************************************
    Public Sub GetLAFacilitiesOutput(ByVal strNAClass As String)
        Dim table As ITable = TryCast(m_NAContext.NAClasses.ItemByName(strNAClass), ITable)
        If table Is Nothing Then
            lstOutput.Items.Add("Impossible to get the " & strNAClass & " table")
        End If

        If table.RowCount(Nothing) > 0 Then
            Dim cursor As ICursor
            Dim row As IRow
            Dim facilityName As String
            Dim demandWeight, total_impedance As Double
            Dim demandCount As Long
            Dim facilityCount As Long = 0
            Dim sumDemand As Long = 0
            Dim sumWeightedDistance As Double = 0.0

            cursor = table.Search(Nothing, False)
            row = cursor.NextRow()
            Do While Not row Is Nothing
                demandCount = Long.Parse(row.Value(table.FindField("DemandCount")).ToString())
                If demandCount > 0 Then
                    facilityCount = facilityCount + 1
                    facilityName = row.Value(table.FindField("Name")).ToString()
                    demandWeight = Double.Parse(row.Value(table.FindField("DemandWeight")).ToString())
                    total_impedance = Double.Parse(row.Value(table.FindField("TotalWeighted_" & cboCostAttribute.Text)).ToString())
                    sumWeightedDistance = sumWeightedDistance + total_impedance
                    sumDemand = sumDemand + demandCount
                End If
                row = cursor.NextRow()
            Loop
            lstOutput.Items.Add("Number of facilities Located " & facilityCount.ToString())
            lstOutput.Items.Add("Number of demand points Allocated " & sumDemand.ToString())
            lstOutput.Items.Add("Sum of weighted " & cboCostAttribute.Text & " " & sumWeightedDistance.ToString())
            lstOutput.Items.Add("")
        End If
        lstOutput.Refresh()
    End Sub

    '*********************************************************************************
    ' Get the Impedance Cost form the LALines Class Output and list out the allocation
    '*********************************************************************************
    Public Sub GetLALinesOutput(ByVal strNAClass As String)
        Dim table As ITable = TryCast(m_NAContext.NAClasses.ItemByName(strNAClass), ITable)
        If table Is Nothing Then
            lstOutput.Items.Add("Impossible to get the " & strNAClass & " table")
        End If
        lstOutput.Items.Add("Allocation Table:")
        If table.RowCount(Nothing) > 0 Then
            lstOutput.Items.Add("DemandID,FacilityID,Weight,TotalWeighted_" & cboCostAttribute.Text)
            Dim total_impedance As Double
            Dim demandID As Long
            Dim facilityID As Long
            Dim facilityWeight As Double
            Dim cursor As ICursor
            Dim row As IRow

            cursor = table.Search(Nothing, False)
            row = cursor.NextRow()
            Do While Not row Is Nothing
                facilityID = Long.Parse(row.Value(table.FindField("FacilityID")).ToString())
                demandID = Long.Parse(row.Value(table.FindField("DemandID")).ToString())
                facilityWeight = Double.Parse(row.Value(table.FindField("Weight")).ToString())
                total_impedance = Double.Parse(row.Value(table.FindField("TotalWeighted_" & cboCostAttribute.Text)).ToString())
                lstOutput.Items.Add(demandID.ToString() & "," & Constants.vbTab + facilityID.ToString() & "," & Constants.vbTab + facilityWeight.ToString() & "," & Constants.vbTab + total_impedance.ToString("F2"))
                row = cursor.NextRow()
            Loop
        End If
        lstOutput.Refresh()
    End Sub

    '*********************************************************************************
    ' 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

    Public Function GetDENetworkDataset(ByVal networkDataset As INetworkDataset) As IDENetworkDataset
        ' Cast from the Network Dataset to the DatasetComponent
        Dim dsComponent As IDatasetComponent = TryCast(networkDataset, IDatasetComponent)

        ' Get the Data Element
        Return TryCast(dsComponent.DataElement, IDENetworkDataset)
    End Function

    '*********************************************************************************
    ' Read the solver settings from the user and solve the Location-Allocation problem
    '*********************************************************************************
    Private Sub cmdSolve_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdSolve.Click
        Dim gpMessages As IGPMessages = New GPMessagesClass()
        Try
            lstOutput.Items.Clear()
            lstOutput.Items.Add("Solving...")

            SetSolverSettings()

            ' Solve
            If (Not m_NAContext.Solver.Solve(m_NAContext, gpMessages, Nothing)) Then
                lstOutput.Items.Add("Partial Result")
            End If

            GetLAFacilitiesOutput("Facilities")
            GetLALinesOutput("LALines")

            'Zoom to the extent of the route
            Dim geoDataset As IGeoDataset = TryCast(m_NAContext.NAClasses.ItemByName("LALines"), IGeoDataset)
            Dim envelope As IEnvelope = geoDataset.Extent
            If (Not envelope.IsEmpty) Then
                envelope.Expand(1.1, 1.1, True)
                axMapControl.Extent = envelope
            End If
            axMapControl.Refresh()

        Catch ee As Exception
            lstOutput.Items.Add("Failure: " + ee.Message)
        End Try

        lstOutput.Items.Add(GetGPMessagesAsString(gpMessages))
        cmdSolve.Text = "Solve"
    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

    '*********************************************************************************
    ' Manage the cutoff, either <None> or some intelligent default
    '*********************************************************************************
    Private Sub cboProblemType_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) Handles cboProblemType.SelectedIndexChanged
        ' All problem types except Minimize Impedance need an impedance cutoff.
        ' So manage an intelligent default other than <None> for them.
        ' If cutoff is set and problem type switches back to Minimize Impedance, reset cutoff to <None>
        ' Note: 3.0 is ok for Minutes but if impedance is something else like Meters, 3.0 will be too small
        ' and cause some solver errors like "Value does not fall within the expected range."
        If cboProblemType.Text.Equals("Minimize Impedance") Then
            If (Not m_ProblemType.Equals("Minimize Impedance")) Then
                If (Not txtCutOff.Text.Equals("<None>")) Then
                    txtCutOff.Text = "<None>"
                End If
            End If
        Else
            If txtCutOff.Text.Equals("<None>") Then
                txtCutOff.Text = "3.0"
            End If
        End If

        m_ProblemType = cboProblemType.Text
    End Sub

    Private Function IsNumeric(ByVal str As String) As Boolean
        Try
            Double.Parse(str.Trim())
        Catch e1 As Exception
            Return False
        End Try
        Return True
    End Function

End Class