Subset network evaluators
SubsetNetworkEvaluators\FilterSubsetEvaluator.cs
// Copyright 2012 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;

namespace SubsetNetworkEvaluators
{
  /// <summary>
  /// The filter subset network evaluator is a custom network evaluator for modeling dynamic restriction attributes
  /// where the subset of elements to restrict can be quickly switched at run-time without having to update the network
  /// dataset schema in ArcCatalog.  In this example the subset of network elements that are restricted is determined
  /// based on the selected network features in ArcMap, but it does not matter how the element subset is determined.
  /// The selected features can be interpreted as the only restricted features or as the only traversable features based
  /// on a parameter flag value, and the subset of elements is also specified as a network attribute parameter value.
  /// </summary>

  [ClassInterface(ClassInterfaceType.None)]
  [Guid("e2a9fbbf-8950-48cb-b487-0ee3f43dccca")]
  [ProgId("SubsetNetworkEvaluators.FilterSubsetEvaluator")]
  public class FilterSubsetEvaluator : INetworkEvaluator2, INetworkEvaluatorSetup
  {
    #region Member Variables

    private INetworkDataset m_networkDataset;
    private INetworkSource m_networkSource;
    private INetworkAttribute m_networkAttribute;

    // Indicates if filter values should be restricted (otherwise NON filter values are restricted)
    // SPECIAL CASE: if NO elements are in the filter, then no elements for THIS SOURCE will be restricted.
    // number of EIDs to filter for this source 
    // the EIDs to override values for, by scaling, for this source
    private bool m_restrictFilterElements = true;

    private int m_countSourceEIDs = 0;
    private Dictionary<int, int> m_sourceEIDHashTable;

    #endregion

    #region INetworkEvaluator Members

    public bool CacheAttribute
    {
      // CacheAttribute returns whether or not we want the network dataset to cache our evaluated attribute values during the network dataset build
      // Since this is a dynamic evaluator, we will return false, so that our attribute values are dynamically queried at runtime
      get { return false; }
    }

    public string DisplayName
    {
      get { return "FilterSubset"; }
    }

    public string Name
    {
      get { return "SubsetNetworkEvaluators.FilterSubset"; }
    }

    #endregion

    #region INetworkEvaluator2 Members

    public void Refresh()
    {
      // This method is called internally during a solve operation immediately prior to performing the actual solve
      // This gives us an opportunity to update our evaluator's internal state based on parameter values

      m_restrictFilterElements = true;
      m_countSourceEIDs = 0;
      m_sourceEIDHashTable = new Dictionary<int, int>();

      INetworkAttribute2 netAttribute2 = m_networkAttribute as INetworkAttribute2;
      IArray netAttributeParams = netAttribute2.Parameters;

      // Parameters: "FilterSubset_Restrict", "FilterSubset_EID_<SourceName>"
      string prefix = BaseParameterName + "_";
      string paramRestrictFilterName = prefix + "Restrict";
      string paramEIDsName = prefix + "EIDs_" + m_networkSource.Name;

      int nParamRestrictFilter = SubsetHelper.FindParameter(netAttributeParams, paramRestrictFilterName);
      int nParamEIDs = SubsetHelper.FindParameter(netAttributeParams, paramEIDsName);

      object value;

      INetworkAttributeParameter paramRestrictFilter;
      INetworkAttributeParameter paramEIDs;

      if (nParamRestrictFilter >= 0)
      {
        paramRestrictFilter = netAttributeParams.get_Element(nParamRestrictFilter) as INetworkAttributeParameter;
        value = paramRestrictFilter.Value;
        if (value != null)
          m_restrictFilterElements = (bool)value;
      }

      if (nParamEIDs >= 0)
      {
        paramEIDs = netAttributeParams.get_Element(nParamEIDs) as INetworkAttributeParameter;
        value = paramEIDs.Value as int[];
        if (value != null)
        {
          int eid;
          int[] rgEIDs;
          rgEIDs = (int[])value;

          int lb = rgEIDs.GetLowerBound(0);
          int ub = rgEIDs.GetUpperBound(0);

          for (int i = lb; i <= ub; ++i)
          {
            ++m_countSourceEIDs;
            eid = rgEIDs[i];
            m_sourceEIDHashTable.Add(eid, eid);
          }
        }
      }
    }

    public IStringArray RequiredFieldNames
    {
      // This custom evaluator does not require any field names
      get { return null; }
    }

    #endregion

    #region INetworkEvaluatorSetup Members

    public UID CLSID
    {
      get
      {
        UID uid = new UIDClass();
        uid.Value = "{e2a9fbbf-8950-48cb-b487-0ee3f43dccca}";
        return uid;
      }
    }

    public IPropertySet Data
    {
      // The Data property is intended to make use of property sets to get/set the custom evaluator's properties using only one call to the evaluator object
      // This custom evaluator does not make use of this property
      get { return null; }
      set { }
    }

    public bool DataHasEdits
    {
      // Since this custom evaluator does not make any data edits, return false
      get { return false; }
    }

    public void Initialize(INetworkDataset networkDataset, IDENetworkDataset DataElement, INetworkSource netSource, IEvaluatedNetworkAttribute netAttribute)
    {
      // Initialize is called once per session (ArcMap session, ArcCatalog session, etc.) to initialize the evaluator for an associated network dataset            
      m_networkDataset = networkDataset;
      m_networkSource = netSource;
      m_networkAttribute = netAttribute;

      Refresh();
    }

    public object QueryValue(INetworkElement Element, IRow Row)
    {
      if (m_countSourceEIDs <= 0)
        return false;

      bool restrict = !m_restrictFilterElements;
      int eid = -1;
      if (m_sourceEIDHashTable.TryGetValue(Element.EID, out eid))
        restrict = m_restrictFilterElements;

      return restrict;
    }

    public bool SupportsDefault(esriNetworkElementType ElementType, IEvaluatedNetworkAttribute netAttribute)
    {
      return false;
    }

    public bool SupportsSource(INetworkSource Source, IEvaluatedNetworkAttribute netAttribute)
    {
      // This custom evaluator supports restriction attributes for all sources
      return netAttribute.UsageType == esriNetworkAttributeUsageType.esriNAUTRestriction;
    }

    public bool ValidateDefault(esriNetworkElementType ElementType, IEvaluatedNetworkAttribute netAttribute, ref int ErrorCode, ref string ErrorDescription, ref string errorAppendInfo)
    {
      if (SupportsDefault(ElementType, netAttribute))
      {
        ErrorCode = 0;
        ErrorDescription = errorAppendInfo = string.Empty;
        return true;
      }
      else
      {
        ErrorCode = -1;
        ErrorDescription = errorAppendInfo = string.Empty;
        return false;
      }
    }

    public bool ValidateSource(IDatasetContainer2 datasetContainer, INetworkSource netSource, IEvaluatedNetworkAttribute netAttribute, ref int ErrorCode, ref string ErrorDescription, ref string errorAppendInfo)
    {
      if (SupportsSource(netSource, netAttribute))
      {
        ErrorCode = 0;
        ErrorDescription = errorAppendInfo = string.Empty;
        return true;
      }
      else
      {
        ErrorCode = -1;
        ErrorDescription = errorAppendInfo = string.Empty;
        return false;
      }
    }

    #endregion

    #region Static Members

    public static string BaseParameterName
    {
      get
      {
        return "FilterSubset";
      }
    }

    public static void RemoveFilterSubsetAttribute(IDENetworkDataset deNet)
    {
      IArray netAttributes = SubsetHelper.RemoveAttributesByPrefix(deNet.Attributes, "Filter");
      deNet.Attributes = netAttributes;
    }

    public static IEvaluatedNetworkAttribute AddFilterSubsetAttribute(IDENetworkDataset deNet)
    {
      IArray netAttributes = deNet.Attributes;
      IEvaluatedNetworkAttribute netAttribute = new EvaluatedNetworkAttributeClass() as IEvaluatedNetworkAttribute;

      netAttribute.Name = BaseParameterName;
      netAttribute.UsageType = esriNetworkAttributeUsageType.esriNAUTRestriction;
      netAttribute.DataType = esriNetworkAttributeDataType.esriNADTBoolean;
      netAttribute.Units = esriNetworkAttributeUnits.esriNAUUnknown;

      INetworkAttribute2 netAttribute2 = netAttribute as INetworkAttribute2;
      netAttribute2.UseByDefault = true;

      List<INetworkSource> allNetSources = SubsetHelper.GetSourceList(deNet.Sources);
      List<INetworkSource> netSources = SubsetHelper.GetSourceList(allNetSources, esriNetworkElementType.esriNETEdge);
      List<string> netSourceNames = SubsetHelper.GetSourceNames(netSources);

      ResetFilterSubsetParameters((INetworkAttribute2)netAttribute, netSourceNames);

      bool supportTurns = deNet.SupportsTurns;

      //default evaluators
      SubsetHelper.SetDefaultEvaluator(netAttribute, false, esriNetworkElementType.esriNETEdge);
      SubsetHelper.SetDefaultEvaluator(netAttribute, false, esriNetworkElementType.esriNETJunction);
      if (supportTurns)
        SubsetHelper.SetDefaultEvaluator(netAttribute, false, esriNetworkElementType.esriNETTurn);

      //sourced evaluators
      foreach (INetworkSource netSource in netSources)
        SubsetHelper.SetEvaluators(netAttribute, netSource, typeof(FilterSubsetEvaluator));

      netAttributes.Add(netAttribute);
      deNet.Attributes = netAttributes;

      return netAttribute;
    }

    public static void ResetFilterSubsetParameters(INetworkAttribute2 netAttribute, List<string> netSourceNames)
    {
      IArray netParams = new ESRI.ArcGIS.esriSystem.ArrayClass();
      INetworkAttributeParameter netParam = null;
      object paramValue = null;

      netParam = new NetworkAttributeParameterClass();
      paramValue = true;

      string paramName = "";

      paramName = BaseParameterName;
      paramName += "_Restrict";

      netParam.Name = paramName;
      netParam.VarType = (int)VarType.Bool;
      netParam.Value = paramValue;
      netParam.DefaultValue = paramValue;
      netParams.Add(netParam);

      netParam = new NetworkAttributeParameterClass();
      paramValue = 1;

      foreach (string netSourceName in netSourceNames)
      {
        netParam = new NetworkAttributeParameterClass();
        paramValue = null;

        paramName = BaseParameterName;
        paramName += "_EIDs_";
        paramName += netSourceName;
        netParam.Name = paramName;
        netParam.VarType = (int)(VarType.Array | VarType.Integer);
        netParam.Value = paramValue;
        netParam.DefaultValue = paramValue;
        netParams.Add(netParam);
      }

      //does not preserve existing parameters if any
      netAttribute.Parameters = netParams;
    }

    #endregion
  }
}