How to load network locations based on network fields (not geometry) (.NET/C#)

317
2
10-10-2013 11:42 AM
Highlighted
New Contributor III
I would like to load network features (e.g. facilities, point barriers).

I cannot get 'load using network fields' to work (I can load based on geometry, but this isn't what I want).

I am using the exact code from (below) from this .NET help topic. I am using 10.2.

How to load data into a network analysis problem
http://resources.arcgis.com/en/help/...023q000000.htm

rowsLocated is always coming back as 0. rowsIn shows that there are indeed rows in my input cursor. I have tried on a couple feature classes, but always the same result.

Any ideas what could cause this to not work? I can load from the same feature classes using ArcMap. Works fine.

I'm not doing anything with GP

Thanks,

-Cory

public void LoadAnalysisObjectsByField(ESRI.ArcGIS.Geodatabase.ITable inputClass,
    string naClassName, ESRI.ArcGIS.NetworkAnalyst.INAContext naContext)
{
    // Both Initialize and Load take a cursor from the input class
    ESRI.ArcGIS.Geodatabase.ICursor cursor = inputClass.Search(null, false)as
        ESRI.ArcGIS.Geodatabase.ICursor;
    ESRI.ArcGIS.NetworkAnalyst.INAClassLoader2 naClassLoader = new
        ESRI.ArcGIS.NetworkAnalyst.NAClassLoaderClass();
    naClassLoader.Initialize(naContext, naClassName, cursor);

    // Store the current set of locator agents, so they can be added back later
    int agentCount = naContext.Locator.LocatorAgentCount;
    var listOfAgents = new System.Collections.Generic.List <
        ESRI.ArcGIS.NetworkAnalyst.INALocatorAgent > ();
    for (int locIndex = 0; locIndex < agentCount; locIndex++)
        listOfAgents.Add(naContext.Locator.get_LocatorAgent(locIndex));

    // Remove the existing locator agents from the locator
    // This for loop is done in reverse order, because agents are being removed as the loop executes
    for (int locIndex = agentCount - 1; locIndex >= 0; locIndex--)
        naContext.Locator.RemoveLocatorAgent(locIndex);

    // Create and add a fields agent
    var fieldsAgent = new
        ESRI.ArcGIS.NetworkAnalyst.NALocatorLocationFieldsAgentClass()as
        ESRI.ArcGIS.NetworkAnalyst.INALocatorLocationFieldsAgent2;

    // Set the field names appropriately based on input data and NAClass
    var naClass = naContext.NAClasses.get_ItemByName(naClassName)as
        ESRI.ArcGIS.NetworkAnalyst.INAClass;
    var naFeatureClass = naClass as ESRI.ArcGIS.Geodatabase.IFeatureClass;

    // Check to see if the NAClass is of type NALocation or NALocationRanges
    ESRI.ArcGIS.esriSystem.UID naLocationFeatureUID = new
        ESRI.ArcGIS.esriSystem.UIDClass();
    naLocationFeatureUID.Value = "esriNetworkAnalyst.NALocationFeature";
    ESRI.ArcGIS.esriSystem.UID naLocationFeatureRangesUID = new
        ESRI.ArcGIS.esriSystem.UIDClass();
    naLocationFeatureRangesUID.Value = "esriNetworkAnalyst.NALocationRangesFeature";
    if (naFeatureClass.CLSID.Compare(naLocationFeatureUID))
    {
        // The field names listed below are the names used in ArcGIS Network Analyst extension classes to represent NALocations.
        //  These are also the names of fields added by the CalculateLocations geoprocessing tool
        fieldsAgent.OIDFieldName = "SourceOID";
        fieldsAgent.SourceIDFieldName = "SourceID";
        fieldsAgent.PositionFieldName = "PosAlong";
        fieldsAgent.SideFieldName = "SideOfEdge";
    }
    else if (naFeatureClass.CLSID.Compare(naLocationFeatureRangesUID))
    {
        // The location ranges input field must be of type BLOB
        fieldsAgent.LocationRangesFieldName = "Locations";
        var blobField = inputClass.Fields.get_Field(inputClass.FindField
            (fieldsAgent.LocationRangesFieldName));
        if (blobField.Type !=
            ESRI.ArcGIS.Geodatabase.esriFieldType.esriFieldTypeBlob)
        {
            System.Windows.Forms.MessageBox.Show(
                "Loading location ranges by field requires a blob field");
            return ;
        }
    }
    naContext.Locator.AddLocatorAgent(fieldsAgent as
        ESRI.ArcGIS.NetworkAnalyst.INALocatorAgent);

    // After Loading is complete, the rowsIn and rowsLocated variable can be used to verify
    //  that every row from the input feature class has been loaded into the network analysis class
    int rowsIn = 0;
    int rowsLocated = 0;
    naClassLoader.Load(cursor, null, ref rowsIn, ref rowsLocated);

    // Now remove the custom fields agent and add back the stored agents
    naContext.Locator.RemoveLocatorAgent(0);
    foreach (var agent in listOfAgents)
        naContext.Locator.AddLocatorAgent(agent);
}
Tags (2)
Reply
0 Kudos
2 Replies
Highlighted
Occasional Contributor III
Hello, Cory!

Looks like your code matches the code sample exactly.  Before I guess at the root problem, here is some background for anyone who might read this thread.

Assuming you are loading Stops in a Route...

There are 4 fields on the Stops class that represent the network location: SourceOID, SourceID, PosAlong, and SideOfEdge. When the solver uses that stop, it doesn't care about the geometry of the stop, it only uses those location fields to determine where along the network the stop is.

When you load by geometry, the class loader takes the input feature's geometry, determines the value of the 4 location fields and populates those fields when the feature is added to the Stops class on the Route.

When you load by location fields, your input features already have those 4 location fields which identify the network location.  There is no need to determine the nearest network location to the geometry, because we already have the 4 fields populated.

Since you are loading by locations and getting zero stops loaded, that might mean that something is up in your input class.

The problem is probably in these lines of code:

                fieldsAgent.OIDFieldName = "SourceOID";
                fieldsAgent.SourceIDFieldName = "SourceID";
                fieldsAgent.PositionFieldName = "PosAlong";
                fieldsAgent.SideFieldName = "SideOfEdge";

Does your input class have "SourceOID", "SourceID", "PosAlong" and "SideOfEdge" fields already?  If not, they need to be there.  If you have them, but have renamed them to something else, then change the above code to reflect the new names.

The purpose of loading by fields is for a slight performance improvement.  We can avoid the spatial search for the nearest network location to your stops. If you have to load the same location over and over, or you have to load hundreds or thousands of locations, loading by field can be very helpful.  In order for this to work, you need to have previously loaded your stops somehow.  And those 4 location fields should already be on your input class. 

If they aren't there, you can use the CalculateLocations GP tool to pre-calculate your location fields.  Or you can load by geometry into a stops class, then export those location fields back onto your input data.

I hope I got the right answer and there isn't a bigger problem going on.  Let me know if this helped.  Good luck!
Reply
0 Kudos
Highlighted
Occasional Contributor III
There was a bug in the SDK code.  Thanks to Cory for tracking it down.  Here is the new and improved load by location fields code:


    public void LoadAnalysisObjectsByField(ESRI.ArcGIS.Geodatabase.ITable inputClass, string naClassName, ESRI.ArcGIS.NetworkAnalyst.INAContext naContext)
    {
      // Create and add a fields agent
      var fieldsAgent = new ESRI.ArcGIS.NetworkAnalyst.NALocatorLocationFieldsAgentClass() as ESRI.ArcGIS.NetworkAnalyst.INALocatorLocationFieldsAgent2;


      // Set the field names appropriately based on input data and NAClass
      var naClass = naContext.NAClasses.get_ItemByName(naClassName) as ESRI.ArcGIS.NetworkAnalyst.INAClass;
      var naFeatureClass = naClass as ESRI.ArcGIS.Geodatabase.IFeatureClass;


      // Check to see if the NAClass is of type NALocation or NALocationRanges
      ESRI.ArcGIS.esriSystem.UID naLocationFeatureUID = new ESRI.ArcGIS.esriSystem.UIDClass();
      naLocationFeatureUID.Value = "esriNetworkAnalyst.NALocationFeature";
      ESRI.ArcGIS.esriSystem.UID naLocationFeatureRangesUID = new ESRI.ArcGIS.esriSystem.UIDClass();
      naLocationFeatureRangesUID.Value = "esriNetworkAnalyst.NALocationRangesFeature";


      if (naFeatureClass.CLSID.Compare(naLocationFeatureUID))
      {
        // The field names listed below are the names used in Network Analyst classes to represent NALocations.
        //  These are also the names of fields added by the CalculateLocations geoprocessing tool
        fieldsAgent.OIDFieldName = "SourceOID";
        fieldsAgent.SourceIDFieldName = "SourceID";
        fieldsAgent.PositionFieldName = "PosAlong";
        fieldsAgent.SideFieldName = "SideOfEdge";
      }
      else if (naFeatureClass.CLSID.Compare(naLocationFeatureRangesUID))
      {
        // The location ranges input field must be of type BLOB
        fieldsAgent.LocationRangesFieldName = "Locations";
        var blobField = inputClass.Fields.get_Field(inputClass.FindField(fieldsAgent.LocationRangesFieldName));
        if (blobField.Type != ESRI.ArcGIS.Geodatabase.esriFieldType.esriFieldTypeBlob)
        {
          System.Windows.Forms.MessageBox.Show("Loading location ranges by field requires a blob field");
          return;
        }
      }


      // Bind for a fields agent will properly associate the agent with the network dataset.
      //  This is useful internally for mapping SourceID back to network sources.
      ((INALocatorAgent)fieldsAgent).Bind(naContext.NetworkDataset, null);


      var posFieldsLocator = new ESRI.ArcGIS.NetworkAnalyst.NALocatorClass() as INALocator;
      posFieldsLocator.FindClosestAmongAllAgents = false;
      posFieldsLocator.SnapToleranceUnits = ESRI.ArcGIS.esriSystem.esriUnits.esriMeters;
      posFieldsLocator.SnapTolerance = 0.0;


      // Bind to network dataset is required. Bind has a side effect of adding feature locator agents for all
      // feature sources. In this case we only want a field locator agent so remove the feature agents after Bind
      // and before adding the field agent to NALocator.
      posFieldsLocator.Bind(naContext.NetworkDataset, null);
      int locatorAgentCount = posFieldsLocator.LocatorAgentCount;
      for (int i = locatorAgentCount - 1; i >= 0; --i)
        posFieldsLocator.RemoveLocatorAgent(i);


      posFieldsLocator.AddLocatorAgent((INALocatorAgent)fieldsAgent);
      ((INAContextEdit)naContext).Locator = posFieldsLocator;


      // Both Initialize and Load take a cursor from the input class
      ESRI.ArcGIS.Geodatabase.ICursor cursor = inputClass.Search(null, false) as ESRI.ArcGIS.Geodatabase.ICursor;


      ESRI.ArcGIS.NetworkAnalyst.INAClassLoader2 naClassLoader = new ESRI.ArcGIS.NetworkAnalyst.NAClassLoaderClass();
      naClassLoader.Initialize(naContext, naClassName, cursor);


      // After Loading is complete, the rowsIn and rowsLocated variable can be used to verify
      //  that every row from the input feature class has been loaded into the network analysis class
      int rowsIn = 0;
      int rowsLocated = 0;
      naClassLoader.Load(cursor, null, ref rowsIn, ref rowsLocated);


      // Now remove the custom fields agent and reset the locator
      naContext.Locator.RemoveLocatorAgent(0);
      naContext.Locator.Bind(naContext.NetworkDataset, null);
    }


Reply
0 Kudos