ArcObjects Library Reference  

frmLocationAllocationSolver

About the Location-allocation solver Sample

[C#]

frmLocationAllocationSolver.cs

//*************************************************************************************
//       Network Analyst - 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;

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("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";

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

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

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

		//*********************************************************************************
		// Network Analyst 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 snapTolerance)
		{
			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 (snapTolerance > 0) classLoader.Locator.SnapTolerance = snapTolerance;
			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;

			INALocationAllocationSolver laSolver = naSolver as INALocationAllocationSolver;

			// 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("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 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)
		{
			try
			{
				lstOutput.Items.Clear();
				lstOutput.Items.Add("Solving...");

				SetSolverSettings();

				// Solve
				IGPMessages gpMessages = new GPMessagesClass();
				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)
			{
				MessageBox.Show(ee.Message);
				cmdSolve.Text = "Choose Optimal Facility Locations";
			}
		}

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

'*************************************************************************************
'       Network Analyst - 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 Microsoft.VisualBasic
Imports System
Imports System.Windows.Forms
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.NetworkAnalyst
Imports ESRI.ArcGIS.esriSystem


	Partial Public Class frmLocationAllocationSolver
		Inherits Form
		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 the geodatabase and network dataset
			Dim featureWorkspace As IFeatureWorkspace = TryCast(OpenWorkspace(Application.StartupPath & "\..\..\..\..\..\Data\SanFrancisco\SanFrancisco.gdb"), IFeatureWorkspace)
			Dim networkDataset As INetworkDataset = OpenNetworkDataset(TryCast(featureWorkspace, IWorkspace), "Transportation", "Streets_ND")

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

			' Populate combo box with Location-Allocation problem types
			cboProblemType.Items.Add("Minimize Impedance")
			cboProblemType.Items.Add("Maximize 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"

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

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

			' Create Layer for Network Dataset and add to Ax Map Control
			Dim layer As ILayer
			Dim networkLayer As INetworkLayer
			networkLayer = New NetworkLayerClass()
			networkLayer.NetworkDataset = networkDataset
			layer = 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

		'*********************************************************************************
		' Network Analyst 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 snapTolerance 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()

			' Create a NAClassLoader and set the snap tolerance (meters unit)
			Dim classLoader As INAClassLoader = New NAClassLoader()
			classLoader.Locator = m_NAContext.Locator
			If snapTolerance > 0 Then
				classLoader.Locator.SnapTolerance = snapTolerance
			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

			' 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
			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 for the Location-Allocation Solver
		'*********************************************************************************
		Public Sub SetSolverSettings()
			'Set Location-Allocation specific Settings
			Dim naSolver As INASolver = m_NAContext.Solver

			Dim laSolver As INALocationAllocationSolver = TryCast(naSolver, INALocationAllocationSolver)

			' 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("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 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

		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

		'*********************************************************************************
		' 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
			Try
				lstOutput.Items.Clear()
				lstOutput.Items.Add("Solving...")

				SetSolverSettings()

				' Solve
				Dim gpMessages As IGPMessages = New GPMessagesClass()
				If (Not m_NAContext.Solver.Solve(m_NAContext, gpMessages, Nothing)) Then
					GetLAFacilitiesOutput("Facilities")
					GetLALinesOutput("LALines")
				Else
					lstOutput.Items.Add("Partial Result")
				End If

				'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)
				End If
				axMapControl.Extent = envelope
				axMapControl.Refresh()

			Catch ee As Exception
				MessageBox.Show(ee.Message)
				cmdSolve.Text = "Choose Optimal Facility Locations"
			End Try
		End Sub

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