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