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