ProSnippets UtilityNetwork - Esri/arcgis-pro-sdk GitHub Wiki

Language:              C#  
Subject:               UtilityNetwork  
Contributor:           ArcGIS Pro SDK Team <[email protected]>  
Organization:          esri, http://www.esri.com  
Date:                  10/22/2024  
ArcGIS Pro:            3.4  
Visual Studio:         2022  
.NET Target Framework: .Net 8  

Obtaining Utility Networks

Get a Utility Network from a Table

public static UtilityNetwork GetUtilityNetworkFromTable(Table table)
{
  UtilityNetwork utilityNetwork = null;

  if (table.IsControllerDatasetSupported())
  {
    // Tables can belong to multiple controller datasets, but at most one of them will be a UtilityNetwork
    IReadOnlyList<Dataset> controllerDatasets = table.GetControllerDatasets();

    foreach (Dataset controllerDataset in controllerDatasets)
    {
      if (controllerDataset is UtilityNetwork)
      {
        utilityNetwork = controllerDataset as UtilityNetwork;
      }
      else
      {
        controllerDataset.Dispose();
      }
    }
  }
  return utilityNetwork;
}

Get a Utility Network from a Layer

// This routine obtains a utility network from a FeatureLayer, SubtypeGroupLayer, or UtilityNetworkLayer
public static UtilityNetwork GetUtilityNetworkFromLayer(Layer layer)
{
  UtilityNetwork utilityNetwork = null;

  if (layer is UtilityNetworkLayer)
  {
    UtilityNetworkLayer utilityNetworkLayer = layer as UtilityNetworkLayer;
    utilityNetwork = utilityNetworkLayer.GetUtilityNetwork();
  }

  else if (layer is SubtypeGroupLayer)
  {
    CompositeLayer compositeLayer = layer as CompositeLayer;
    utilityNetwork = GetUtilityNetworkFromLayer(compositeLayer.Layers.First());
  }

  else if (layer is FeatureLayer)
  {
    FeatureLayer featureLayer = layer as FeatureLayer;
    using (FeatureClass featureClass = featureLayer.GetFeatureClass())
    {
      if (featureClass.IsControllerDatasetSupported())
      {
        IReadOnlyList<Dataset> controllerDatasets = new List<Dataset>();
        controllerDatasets = featureClass.GetControllerDatasets();
        foreach (Dataset controllerDataset in controllerDatasets)
        {
          if (controllerDataset is UtilityNetwork)
          {
            utilityNetwork = controllerDataset as UtilityNetwork;
          }
          else
          {
            controllerDataset.Dispose();
          }
        }
      }
    }
  }
  return utilityNetwork;
}

Elements

Fetching a Row from an Element

// usage :   using (var row = FetchRowFromElement(...))
public static Row FetchRowFromElement(UtilityNetwork utilityNetwork, Element element)
{
  // Get the table from the element
  using (Table table = utilityNetwork.GetTable(element.NetworkSource))
  {
    // Create a query filter to fetch the appropriate row
    QueryFilter queryFilter = new QueryFilter()
    {
      ObjectIDs = new List<long>() { element.ObjectID }
    };

    // Fetch and return the row
    using (RowCursor rowCursor = table.Search(queryFilter))
    {
      if (rowCursor.MoveNext())
      {
        return rowCursor.Current;
      }
      return null;
    }
  }
}

Get percentage along a polyline

public double GetPolylineStartingPercentage(Element startingPointElement, Geometry clickedPoint, FeatureLayer featureLayer)
{
  // Find feature            
  QueryFilter queryFilter = new QueryFilter()
  {
    ObjectIDs = new List<long>() { startingPointElement.ObjectID }
  };


  using (RowCursor rowCursor = featureLayer.GetFeatureClass().Search(queryFilter, false))
  {
    if (!rowCursor.MoveNext())
    {
      return 0;
    }

    using (Row row = rowCursor.Current)
    {
      try
      {
        Feature feature = row as Feature;

        Polyline polyline = feature.GetShape() as Polyline;
        ICollection<Segment> segments = new List<Segment>();
        polyline.GetAllSegments(ref segments);

        IList<Segment> segments2 = segments as IList<Segment>;

        MapPoint startPoint = segments2[0].StartPoint;
        MapPoint endPoint = segments2[segments2.Count - 1].EndPoint;
        MapPoint clickedMapPoint = clickedPoint as MapPoint;


        LineSegment line = LineBuilderEx.CreateLineSegment(startPoint, endPoint);
        MapPoint outPoint = GeometryEngine.Instance.QueryPointAndDistance(line, SegmentExtensionType.NoExtension, clickedMapPoint, AsRatioOrLength.AsRatio,
            out double distanceAlongCurve, out double distanceFromCurve, out LeftOrRightSide side);


        return distanceAlongCurve;
      }
      catch (Exception e)
      {
        // Handle exceptions
      }
    }
  }
  return 0;
}

Editing Associations

Create a utility network association

// Create edit operation
EditOperation editOperation = new EditOperation();
editOperation.Name = "Create structural attachment association";

// Create a RowHandle for the pole

Element poleElement = utilityNetwork.CreateElement(poleAssetType, poleGlobalID);
RowHandle poleRowHandle = new RowHandle(poleElement, utilityNetwork);

// Create a RowHandle for the transformer bank

Element transformerBankElement = utilityNetwork.CreateElement(transformerBankAssetType, transformerBankGlobalID);
RowHandle transformerBankRowHandle = new RowHandle(transformerBankElement, utilityNetwork);

// Attach the transformer bank to the pole

AssociationDescription structuralAttachmentAssociationDescription = new AssociationDescription(AssociationType.Attachment, poleRowHandle, transformerBankRowHandle);
editOperation.Create(structuralAttachmentAssociationDescription);
editOperation.Execute();

Create utility network features and associations in a single edit operation

// Create an EditOperation
EditOperation editOperation = new EditOperation();
editOperation.Name = "Create pole; create transformer bank; attach transformer bank to pole";

// Create the transformer bank
RowToken transformerBankToken = editOperation.Create(transformerBankLayer, transformerBankAttributes);

// Create a pole
RowToken poleToken = editOperation.Create(poleLayer, poleAttributes);

// Create a structural attachment association between the pole and the transformer bank
RowHandle poleHandle = new RowHandle(poleToken);
RowHandle transformerBankHandle = new RowHandle(transformerBankToken);

AssociationDescription poleAttachment = new AssociationDescription(AssociationType.Attachment, poleHandle, transformerBankHandle);

editOperation.Create(poleAttachment);

// Execute the EditOperation
editOperation.Execute();

Traverse Associations

Get traverse associations result from downward traversal

public static void GetTraverseAssociationsResultFromDownwardTraversal(UtilityNetwork utilityNetwork, IReadOnlyList<Element> startingElements)
{
  // Set downward traversal with maximum depth
  TraverseAssociationsDescription traverseAssociationsDescription = new TraverseAssociationsDescription(TraversalDirection.Descending);

  // Get traverse associations result from the staring element up to maximum depth
  TraverseAssociationsResult traverseAssociationsResult = utilityNetwork.TraverseAssociations(startingElements, traverseAssociationsDescription);

  // Get associations participated in traversal
  IReadOnlyList<Association> associations = traverseAssociationsResult.Associations;
}

Get traverse associations result from upward traversal with depth limit

public static void GetTraverseAssociationsResultFromUpwardTraversalWithDepthLimit(UtilityNetwork utilityNetwork, IReadOnlyList<Element> startingElements)
{
  // List of fields whose values will be fetched as name-values pairs during association traversal operation
  List<string> additionalFieldsToFetch = new List<string> { "ObjectId", "AssetName", "AssetGroup", "AssetType" };

  // Set downward traversal with maximum depth level of 3 
  TraverseAssociationsDescription traverseAssociationsDescription = new TraverseAssociationsDescription(TraversalDirection.Ascending, 3)
  {
    AdditionalFields = additionalFieldsToFetch
  };

  // Get traverse associations result from the staring element up to depth level 3
  TraverseAssociationsResult traverseAssociationsResult = utilityNetwork.TraverseAssociations(startingElements, traverseAssociationsDescription);

  // List of associations participated in traversal
  IReadOnlyList<Association> associations = traverseAssociationsResult.Associations;

  // KeyValue mapping between involved elements and their field name-values 
  //At 2.x - IReadOnlyDictionary<Element, IReadOnlyList<AssociationElementFieldValue>> associationElementValuePairs = traverseAssociationsResult.AdditionalFieldValues;
  IReadOnlyDictionary<Element, IReadOnlyList<FieldValue>> associationElementValuePairs =
    traverseAssociationsResult.AdditionalFieldValues;

  foreach (KeyValuePair<Element, IReadOnlyList<FieldValue>> keyValuePair in associationElementValuePairs)
  {
    // Element 
    Element element = keyValuePair.Key;

    // List of field names and their values 
    //At 2.x - IReadOnlyList<AssociationElementFieldValue> elementFieldValues = keyValuePair.Value;
    IReadOnlyList<FieldValue> elementFieldValues = keyValuePair.Value;
  }
}

Subnetworks and Tiers

Find a Tier given a Domain Network name and Tier name

using (UtilityNetworkDefinition utilityNetworkDefinition = utilityNetwork.GetDefinition())
{
  DomainNetwork domainNetwork = utilityNetworkDefinition.GetDomainNetwork(domainNetworkName);
  Tier tier = domainNetwork.GetTier(tierName);
}

Update all dirty subnetworks in a tier

using (SubnetworkManager subnetworkManager = utilityNetwork.GetSubnetworkManager())
{
  subnetworkManager.UpdateAllSubnetworks(tier, true);

  mapView.Redraw(true);
}

Life cycle for a simple radial subnetwork with one controller

// Create a subnetwork named "Radial1" with a single controller
// elementR1 represents the device that serves as the subnetwork controller (e.g., circuit breaker)
Subnetwork subnetworkRadial1 = subnetworkManager.EnableControllerInEditOperation(mediumVoltageTier, elementR1, "Radial1", "R1", "my description", "my notes");

// ...

// Update the subnetwork and refresh the map
subnetworkRadial1.Update();
MapView.Active.Redraw(true);

// ...

// At some point, a subnetwork will need to be deleted.

// First step is to disable the controller
subnetworkManager.DisableControllerInEditOperation(elementR1);

// At this point, the subnetwork is deleted, but all of the rows that have been labeled with the subnetwork ID need to be updated
subnetworkRadial1.Update();
MapView.Active.Redraw(true);

// The final step is to notify external systems (if any) by exporting the subnetwork
SubnetworkExportOptions subnetworkExportOptions = new SubnetworkExportOptions()
{
  SetAcknowledged = true,
  IncludeDomainDescriptions = true,
  IncludeGeometry = true,
  ServiceSynchronizationType = ServiceSynchronizationType.Asynchronous,

  SubnetworkExportResultTypes = new List<SubnetworkExportResultType>()
  {
    SubnetworkExportResultType.Features
  }
  
  // Set networks attributes and attribute fields to export
  //ResultNetworkAttributes = new List<NetworkAttribute>(networkAttributes),

  //ResultFieldsByNetworkSourceID = new Dictionary<int, List<string>>()
  //  { { electricDevice.ID, new List<string>() { "AssetID" } } }

};
subnetworkRadial1.Export(new Uri($"{Path.GetTempPath()}SubnetworkExportResult.json"), subnetworkExportOptions);

Life cycle for a mesh subnetwork with multiple controllers

// Create a subnetwork named "Mesh1" from three controllers
// elementM1, elementM2, and elementM3 represent the devices that serve as subnetwork controllers (e.g., network protectors)
subnetworkManager.EnableController(lowVoltageMeshTier, elementM1, "Mesh1", "M1", "my description", "my notes");
subnetworkManager.EnableController(lowVoltageMeshTier, elementM2, "Mesh1", "M2", "my description", "my notes");
Subnetwork subnetworkMesh1 = subnetworkManager.EnableController(lowVoltageMeshTier, elementM3, "Mesh1", "M3", "my description", "my notes");
subnetworkMesh1.Update();
MapView.Active.Redraw(true);

// ...

// When deleting the subnetwork, each controller must be disabled before the subnetwork itself is deleted
subnetworkManager.DisableControllerInEditOperation(elementM1);
subnetworkManager.DisableControllerInEditOperation(elementM2);
subnetworkManager.DisableControllerInEditOperation(elementM3);

// After the subnetwork is deleted, all of the rows that have been labeled with the subnetwork ID need to be updated
subnetworkMesh1.Update();
MapView.Active.Redraw(true);

// The final step is to notify external systems (if any) by exporting the subnetwork
SubnetworkExportOptions subnetworkExportOptions = new SubnetworkExportOptions()
{
  SetAcknowledged = true,
  IncludeDomainDescriptions = true,
  IncludeGeometry = true,
  ServiceSynchronizationType = ServiceSynchronizationType.Asynchronous,

  SubnetworkExportResultTypes = new List<SubnetworkExportResultType>()
  {
    SubnetworkExportResultType.Features
  }
  
  // Set networks attributes and attribute fields to export
  //ResultNetworkAttributes = new List<NetworkAttribute>(networkAttributes),

  //ResultFieldsByNetworkSourceID = new Dictionary<int, List<string>>()
  //  { { electricDevice.ID, new List<string>() { "AssetID" } } }

};
subnetworkMesh1.Export(new Uri($"{Path.GetTempPath()}SubnetworkExportResult.json"), subnetworkExportOptions);

Life cycle for a multifeed radial subnetwork with two controllers

// Create a subnetwork named "R2, R3" from two controllers
// elementR2 and elementR3 represent the devices that serve as subnetwork controllers (e.g., circuit breakers)
subnetworkManager.EnableControllerInEditOperation(mediumVoltageTier, elementR2, "R2, R3", "R2", "my description", "my notes");
subnetworkManager.EnableControllerInEditOperation(mediumVoltageTier, elementR3, "R2, R3", "R3", "my description", "my notes");

// If the tie switch between them is opened, the original subnetwork controllers must be disabled and re-enabled with different names
// This will create two new subnetworks, named "R2" and "R3"
subnetworkManager.DisableControllerInEditOperation(elementR2);
subnetworkManager.DisableControllerInEditOperation(elementR3);

Subnetwork subnetworkR2 = subnetworkManager.EnableControllerInEditOperation(mediumVoltageTier, elementR2, "R2", "R2", "my description", "my notes");
Subnetwork subnetworkR3 = subnetworkManager.EnableControllerInEditOperation(mediumVoltageTier, elementR3, "R3", "R3", "my description", "my notes");

subnetworkR2.Update();
subnetworkR3.Update();
MapView.Active.Redraw(true);

Export Subnetwork

public void ExportSubnetwork(UtilityNetwork utilityNetwork, string subnetworkName, Uri exportResultJsonPath)
{
  using (UtilityNetworkDefinition utilityNetworkDefinition = utilityNetwork.GetDefinition())
  using (SubnetworkManager subnetworkManager = utilityNetwork.GetSubnetworkManager())
  {
    Subnetwork subnetwork = subnetworkManager.GetSubnetwork(subnetworkName);

    IReadOnlyList<NetworkAttribute> networkAttributes = utilityNetworkDefinition.GetNetworkAttributes();
    IReadOnlyList<NetworkSource> networkSources = utilityNetworkDefinition.GetNetworkSources();

    NetworkSource electricDevice = networkSources.First(f => f.Name.Contains("ElectricDevice"));

    // Export options
    SubnetworkExportOptions subnetworkExportOptions = new SubnetworkExportOptions()
    {
      SetAcknowledged = false,
      IncludeDomainDescriptions = true,
      IncludeGeometry = true,
      ServiceSynchronizationType = ServiceSynchronizationType.Asynchronous,

      SubnetworkExportResultTypes = new List<SubnetworkExportResultType>()
      {
        SubnetworkExportResultType.Connectivity,
        SubnetworkExportResultType.Features
      },

      ResultNetworkAttributes = new List<NetworkAttribute>(networkAttributes),

      ResultFieldsByNetworkSourceID = new Dictionary<int, List<string>>()
        { { electricDevice.ID, new List<string>() { "AssetID" } } }
    };


    subnetwork.Export(exportResultJsonPath, subnetworkExportOptions);
  }
}

Tracing

Create a DownstreamTracer

using (TraceManager traceManager = utilityNetwork.GetTraceManager())
{
  DownstreamTracer downstreamTracer = traceManager.GetTracer<DownstreamTracer>();
}

Create a Trace Argument

IReadOnlyList<Element> startingPointList = new List<Element>();

// Code to fill in list of starting points goes here...
TraceArgument traceArgument = new TraceArgument(startingPointList);

TraceConfiguration traceConfiguration = new TraceConfiguration();

// Code to fill in trace configuration goes here...
traceArgument.Configuration = traceConfiguration;

Create a Condition to compare a Network Attribute against a set of values

// Create a NetworkAttribute object for the Lifecycle network attribute from the UtilityNetworkDefinition
using (NetworkAttribute lifecycleNetworkAttribute = utilityNetworkDefinition.GetNetworkAttribute("Lifecycle"))
{
  // Create a NetworkAttributeComparison that stops traversal if Lifecycle <> "In Design" (represented by the constant InDesign)
  NetworkAttributeComparison inDesignNetworkAttributeComparison = new NetworkAttributeComparison(lifecycleNetworkAttribute, Operator.NotEqual, InDesign);

  // Create a NetworkAttributeComparison to stop traversal if Lifecycle <> "In Service" (represented by the constant InService)
  NetworkAttributeComparison inServiceNetworkAttributeComparison = new NetworkAttributeComparison(lifecycleNetworkAttribute, Operator.NotEqual, InService);

  // Combine these two comparisons together with "And"
  And lifecycleFilter = new And(inDesignNetworkAttributeComparison, inServiceNetworkAttributeComparison);

  // Final condition stops traversal if Lifecycle <> "In Design" and Lifecycle <> "In Service"
  traceConfiguration.Traversability.Barriers = lifecycleFilter;
}

Create a Function

// Get a NetworkAttribute object for the Load network attribute from the UtilityNetworkDefinition
using (NetworkAttribute loadNetworkAttribute = utilityNetworkDefinition.GetNetworkAttribute("Load"))
{
  // Create a function to sum the Load
  Add sumLoadFunction = new Add(loadNetworkAttribute);

  // Add this function to our trace configuration
  traceConfiguration.Functions = new List<Function>() { sumLoadFunction };
}

Create a FunctionBarrier

// Create a NetworkAttribute object for the Shape length network attribute from the UtilityNetworkDefinition
using (NetworkAttribute shapeLengthNetworkAttribute = utilityNetworkDefinition.GetNetworkAttribute("Shape length"))
{
  // Create a function that adds up shape length
  Add lengthFunction = new Add(shapeLengthNetworkAttribute);

  // Create a function barrier that stops traversal after 1000 feet
  FunctionBarrier distanceBarrier = new FunctionBarrier(lengthFunction, Operator.GreaterThan, 1000.0);

  // Set this function barrier
  traceConfiguration.Traversability.FunctionBarriers = new List<FunctionBarrier>() { distanceBarrier };
}

Create an output condition

// Create an output category to filter the trace results to only include
// features with the "Service Point" category assigned
traceConfiguration.OutputCondition = new CategoryComparison(CategoryOperator.IsEqual, "Service Point");

Create a Propagator

// Get a NetworkAttribute object for the Phases Normal attribute from the UtilityNetworkDefinition
using (NetworkAttribute normalPhaseAttribute = utilityNetworkDefinition.GetNetworkAttribute("Phases Normal"))
{
  // Create a propagator to propagate the Phases Normal attribute downstream from the source, using a Bitwise And function
  // Allow traversal to continue as long as the Phases Normal value includes any of the ABC phases
  // (represented by the constant ABCPhase)
  Propagator phasePropagator = new Propagator(normalPhaseAttribute, PropagatorFunction.BitwiseAnd, Operator.IncludesAny, ABCPhase);

  // Assign this propagator to our trace configuration
  traceConfiguration.Propagators = new List<Propagator>() { phasePropagator };
}

Using Function Results

// Get the FunctionOutputResult from the trace results
FunctionOutputResult functionOutputResult = traceResults.OfType<FunctionOutputResult>().First();

// First() can be used here if only one Function was included in the TraceConfiguration.Functions collection.
// Otherwise you will have to search the list for the correct FunctionOutput object.
FunctionOutput functionOutput = functionOutputResult.FunctionOutputs.First();

// Extract the total load from the GlobalValue property
double totalLoad = (double)functionOutput.Value;

Fetch a named trace configuration by name

private NamedTraceConfiguration GetNamedTraceConfigurationsByName(UtilityNetwork utilityNetwork, string configurationName = "WaterNetwork")
{
  // Query to find named trace configurations
  NamedTraceConfigurationQuery namedTraceConfigurationQuery = new NamedTraceConfigurationQuery { Names = new List<string> { configurationName } };

  // Get the trace manager from the utility network
  using (TraceManager traceManager = utilityNetwork.GetTraceManager())
  {
    // A set of named trace configurations specified by the named traced configuration query 
    IReadOnlyList<NamedTraceConfiguration> namedTraceConfigurations = traceManager.GetNamedTraceConfigurations(namedTraceConfigurationQuery);
    NamedTraceConfiguration waterConfiguration = namedTraceConfigurations.First(f => f.Description.Equals(configurationName));

    return waterConfiguration;
  }
}

Fetch named trace configurations from a utility network layer

private NamedTraceConfiguration GetNamedTraceConfigurationsFromUtilityNetworkLayer(UtilityNetworkLayer utilityNetworkLayer, string configurationName = "WaterNetwork")
{
  // Get all named trace configurations in the utility network
  IReadOnlyList<NamedTraceConfiguration> namedTraceConfigurations = utilityNetworkLayer.GetNamedTraceConfigurations();

  foreach (NamedTraceConfiguration namedTraceConfiguration in namedTraceConfigurations)
  {
    if (namedTraceConfiguration.Name == configurationName)
    {
      return namedTraceConfiguration;
    }
  }
  return null;
}

Trace a utility network using a named trace configuration

private void TraceUtilityNetworkUsingNamedTraceConfiguration(UtilityNetwork utilityNetwork, NamedTraceConfiguration namedTraceConfiguration, Element startElement)
{
  // Get the trace manager from the utility network
  using (TraceManager traceManager = utilityNetwork.GetTraceManager())
  {
    // Get a tracer from the trace manager using the named trace configuration
    Tracer upstreamTracer = traceManager.GetTracer(namedTraceConfiguration);

    // Trace argument holding the trace input parameters
    TraceArgument upstreamTraceArgument = new TraceArgument(namedTraceConfiguration, new List<Element> { startElement });

    // Trace results 
    IReadOnlyList<Result> upstreamTraceResults = upstreamTracer.Trace(upstreamTraceArgument);
  }
}

Trace a utility network with the digitized direction

private void TraceWithDigitizedDirection(UtilityNetwork utilityNetwork, Element startElement)
{
  using TraceManager traceManager = utilityNetwork.GetTraceManager();

  // Trace configuration with digitized direction 
  TraceConfiguration traceConfiguration = new TraceConfiguration()
  {
    IncludeIsolatedFeatures = true,
    IncludeBarriersWithResults = true,
    UseDigitizedDirection = true
  };

  // Trace argument
  List<Element> startElements = new List<Element> { startElement };

  TraceArgument traceArgument = new TraceArgument(startElements);
  traceArgument.Configuration = traceConfiguration;

  // Results
  DownstreamTracer downstreamTracer = traceManager.GetTracer<DownstreamTracer>();

  IReadOnlyList<Result> traceResults = downstreamTracer.Trace(traceArgument);
  foreach (Result traceResult in traceResults)
  {
    // Iterate trace results
  }
}

Export a utility network trace as a JSON file

private void ExportUtilityNetworkTraceAsJSON(UtilityNetwork utilityNetwork)
{
  using (TraceManager traceManager = utilityNetwork.GetTraceManager())
  using (UtilityNetworkDefinition utilityNetworkDefinition = utilityNetwork.GetDefinition())
  using (NetworkSource deviceNetworkSource = GetNetworkSource(utilityNetworkDefinition, "GasDevice") as NetworkSource)
  using (FeatureClass distributionDeviceFeatureClass = utilityNetwork.GetTable(deviceNetworkSource) as FeatureClass)
  using (FeatureClassDefinition distributionDeviceDefinition = distributionDeviceFeatureClass.GetDefinition())
  using (AssetGroup deviceAssetGroup = deviceNetworkSource.GetAssetGroup("Regulator"))
  using (AssetType deviceAssetType = deviceAssetGroup.GetAssetType("Pressure Reducing"))
  using (NetworkAttribute deviceStatusNetworkAttribute = utilityNetworkDefinition.GetNetworkAttribute("DeviceStatus"))
  using (NetworkAttribute accessibleNetworkAttribute = utilityNetworkDefinition.GetNetworkAttribute("Accessible"))
  {
    // Domain and tier information
    DomainNetwork domainNetwork = utilityNetworkDefinition.GetDomainNetwork("Gas");
    Tier pipeDistributionSystemTier = domainNetwork.GetTier("Pipe Distribution");

    // Start elements
    Element startingPoint1 = utilityNetwork.CreateElement(deviceAssetType, Guid.Parse("{28CF437E-950C-41B7-B839-8BC45570DE40}"));
    Element startingPoint2 = utilityNetwork.CreateElement(deviceAssetType, Guid.Parse("{63C22828-7BC9-49ED-A14A-A596559B6CB3}"));

    startingPoint1.Terminal = startingPoint1.AssetType.GetTerminalConfiguration().Terminals.First(x => x.IsUpstreamTerminal);
    startingPoint2.Terminal = startingPoint2.AssetType.GetTerminalConfiguration().Terminals.First(x => x.IsUpstreamTerminal);

    List<Element> startingPoints = new List<Element>() { startingPoint1, startingPoint2 };
    List<Element> barriers = new List<Element>();

    // Set up trace filter: DeviceStatus = Open (1) AND Accessible = 1
    NetworkAttributeComparison statusNetworkAttributeComparison = new NetworkAttributeComparison(deviceStatusNetworkAttribute, Operator.Equal, 1);
    NetworkAttributeComparison networkAttributeComparison = new NetworkAttributeComparison(accessibleNetworkAttribute, Operator.Equal, 1);
    
    // Set trace configuration 
    TraceConfiguration traceConfiguration = new TraceConfiguration
    {
      AllowIndeterminateFlow = true,
      IgnoreBarriersAtStartingPoints = false,
      IncludeBarriersWithResults = true,
      IncludeContainers = true,
      IncludeContent = true,
      IncludeIsolatedFeatures = false,
      IncludeUpToFirstSpatialContainer = false
    };

    traceConfiguration.Filter.Barriers = new And(statusNetworkAttributeComparison, networkAttributeComparison);
    traceConfiguration.DomainNetwork = domainNetwork;
    traceConfiguration.SourceTier = pipeDistributionSystemTier;

    // Attribute fields of a network source
    List<string> deviceFields = distributionDeviceDefinition.GetFields().Select(f => f.Name).ToList();

    // Network attributes 
    List<string> networkattributeNames = new List<string>();

    IReadOnlyList<NetworkAttribute> networkAttributes = utilityNetworkDefinition.GetNetworkAttributes();

    foreach (NetworkAttribute networkAttribute in networkAttributes)
    {
      networkattributeNames.Add(networkAttribute.Name);
    }

    // Result Types
    List<ResultType> resultTypeList = new List<ResultType>() { ResultType.Feature };

    // Resutl Options
    ResultOptions resultOptions = new ResultOptions()
    {
      IncludeGeometry = true,
      NetworkAttributes = networkattributeNames,
      ResultFields = new Dictionary<NetworkSource, List<string>>() { { deviceNetworkSource, deviceFields } }
    };

    // Trace Arguments
    TraceArgument traceArgument = new TraceArgument(startingPoints)
    {
      Barriers = barriers,
      Configuration = traceConfiguration,
      ResultTypes = resultTypeList,
      ResultOptions = resultOptions
    };

    ConnectedTracer connectedTracer = traceManager.GetTracer<ConnectedTracer>();

    // Set export options 
    TraceExportOptions exportOptions = new TraceExportOptions()
    {
      ServiceSynchronizationType = ServiceSynchronizationType.Asynchronous,
      IncludeDomainDescriptions = true,
    };

    // Path to export JSON
    string jsonPath =  $"{Path.GetTempPath()}TraceResults.json";
    Uri jsonUri = new Uri(jsonPath);

    // Export 
    connectedTracer.Export(jsonUri, traceArgument, exportOptions);

    string jsonAbsolutePath = HttpUtility.UrlDecode(jsonUri.AbsolutePath);
    if (jsonUri.IsFile && File.Exists(jsonAbsolutePath))
    {
      // Work with the JSON results
    }
  }
  NetworkSource GetNetworkSource(UtilityNetworkDefinition unDefinition, string name)
  {
    IReadOnlyList<NetworkSource> allSources = unDefinition.GetNetworkSources();
    foreach (NetworkSource source in allSources)
    {
      if (name.Contains("Partitioned Sink"))
      {
        if (source.Name.Replace(" ", "").ToUpper().Contains(name.Replace(" ", "").ToUpper()) ||
            source.Name.Replace(" ", "").ToUpper()
              .Contains(name.Replace("Partitioned Sink", "Part_Sink").Replace(" ", "").ToUpper()))
        {
          return source;
        }
      }

      if (name.Contains("Hierarchical Sink"))
      {
        if (source.Name.Replace(" ", "").ToUpper().Contains(name.Replace(" ", "").ToUpper()) ||
            source.Name.Replace(" ", "").ToUpper()
              .Contains(name.Replace("Hierarchical Sink", "Hier_Sink").Replace(" ", "").ToUpper()))
        {
          return source;
        }
      }

      if (source.Name.Replace(" ", "").ToUpper().Contains(name.Replace(" ", "").ToUpper()))
      {
        return source;
      }
    }

    return null;
  }
}

Fetch features and network attributes from a utility network during trace

private void FetchFeaturesAndAttributes(UtilityNetwork utilityNetwork)
{
  using (TraceManager traceManager = utilityNetwork.GetTraceManager())
  using (UtilityNetworkDefinition utilityNetworkDefinition = utilityNetwork.GetDefinition())
  using (NetworkSource deviceNetworkSource = GetNetworkSource(utilityNetworkDefinition, "GasDevice") as NetworkSource)
  using (FeatureClass distributionDeviceFeatureClass = utilityNetwork.GetTable(deviceNetworkSource) as FeatureClass)
  using (FeatureClassDefinition distributionDeviceDefinition = distributionDeviceFeatureClass.GetDefinition())
  using (AssetGroup deviceAssetGroup = deviceNetworkSource.GetAssetGroup("Regulator"))
  using (AssetType deviceAssetType = deviceAssetGroup.GetAssetType("Pressure Reducing"))
  using (NetworkAttribute deviceStatusNetworkAttribute = utilityNetworkDefinition.GetNetworkAttribute("DeviceStatus"))
  using (NetworkAttribute accessibleNetworkAttribute = utilityNetworkDefinition.GetNetworkAttribute("Accessible"))
  {
    // Domain and tier information
    DomainNetwork domainNetwork = utilityNetworkDefinition.GetDomainNetwork("Gas");
    Tier pipeDistributionSystemTier = domainNetwork.GetTier("Pipe Distribution");

    // Start elements
    Element startingPoint1 = utilityNetwork.CreateElement(deviceAssetType, Guid.Parse("{28CF437E-950C-41B7-B839-8BC45570DE40}"));
    Element startingPoint2 = utilityNetwork.CreateElement(deviceAssetType, Guid.Parse("{63C22828-7BC9-49ED-A14A-A596559B6CB3}"));

    startingPoint1.Terminal = startingPoint1.AssetType.GetTerminalConfiguration().Terminals.First(x => x.IsUpstreamTerminal);
    startingPoint2.Terminal = startingPoint2.AssetType.GetTerminalConfiguration().Terminals.First(x => x.IsUpstreamTerminal);

    List<Element> startingPoints = new List<Element>() { startingPoint1, startingPoint2 };
    List<Element> barriers = new List<Element>();

    // Set up trace filter: DeviceStatus = Open (1) AND Accessible = 1
    NetworkAttributeComparison statusNetworkAttributeComparison = new NetworkAttributeComparison(deviceStatusNetworkAttribute, Operator.Equal, 1);
    NetworkAttributeComparison networkAttributeComparison = new NetworkAttributeComparison(accessibleNetworkAttribute, Operator.Equal, 1);
    
    // Set trace configuration 
    TraceConfiguration traceConfiguration = new TraceConfiguration
    {
      AllowIndeterminateFlow = true,
      IgnoreBarriersAtStartingPoints = false,
      IncludeBarriersWithResults = true,
      IncludeContainers = true,
      IncludeContent = true,
      IncludeIsolatedFeatures = false,
      IncludeUpToFirstSpatialContainer = false
    };

    traceConfiguration.Filter.Barriers = new And(statusNetworkAttributeComparison, networkAttributeComparison);
    traceConfiguration.DomainNetwork = domainNetwork;
    traceConfiguration.SourceTier = pipeDistributionSystemTier;

    // Attribute fields of a network source
    List<string> deviceFields = distributionDeviceDefinition.GetFields().Select(f => f.Name).ToList();

    // Network attributes 
    List<string> networkattributeNames = new List<string>();

    IReadOnlyList<NetworkAttribute> networkAttributes = utilityNetworkDefinition.GetNetworkAttributes();

    foreach (NetworkAttribute networkAttribute in networkAttributes)
    {
      networkattributeNames.Add(networkAttribute.Name);
    }

    // Result Types
    List<ResultType> resultTypeList = new List<ResultType>() { ResultType.Feature };

    // Resutl Options
    ResultOptions resultOptions = new ResultOptions()
    {
      IncludeGeometry = true,
      NetworkAttributes = networkattributeNames,
      ResultFields = new Dictionary<NetworkSource, List<string>>() { { deviceNetworkSource, deviceFields } }
    };

    // Trace Arguments
    TraceArgument traceArgument = new TraceArgument(startingPoints)
    {
      Barriers = barriers,
      Configuration = traceConfiguration,
      ResultTypes = resultTypeList,
      ResultOptions = resultOptions
    };

    // Tracer 
    ConnectedTracer connectedTracer = traceManager.GetTracer<ConnectedTracer>();

    // Async trace result
    IReadOnlyList<Result> traceResults = connectedTracer.Trace(traceArgument, ServiceSynchronizationType.Asynchronous);

    // Iterate trace results
    foreach (Result traceResult in traceResults)
    {
      if (traceResult is FeatureElementResult featureElementResult)
      {
        IReadOnlyList<FeatureElement> featureElements = featureElementResult.FeatureElements;
      }
    }
  }
  // Helper inline function
  NetworkSource GetNetworkSource(UtilityNetworkDefinition unDefinition, string name)
  {
    IReadOnlyList<NetworkSource> allSources = unDefinition.GetNetworkSources();
    foreach (NetworkSource source in allSources)
    {
      if (name.Contains("Partitioned Sink"))
      {
        if (source.Name.Replace(" ", "").ToUpper().Contains(name.Replace(" ", "").ToUpper()) ||
            source.Name.Replace(" ", "").ToUpper()
              .Contains(name.Replace("Partitioned Sink", "Part_Sink").Replace(" ", "").ToUpper()))
        {
          return source;
        }
      }

      if (name.Contains("Hierarchical Sink"))
      {
        if (source.Name.Replace(" ", "").ToUpper().Contains(name.Replace(" ", "").ToUpper()) ||
            source.Name.Replace(" ", "").ToUpper()
              .Contains(name.Replace("Hierarchical Sink", "Hier_Sink").Replace(" ", "").ToUpper()))
        {
          return source;
        }
      }

      if (source.Name.Replace(" ", "").ToUpper().Contains(name.Replace(" ", "").ToUpper()))
      {
        return source;
      }
    }

    return null;
  }
}

Get selected features from a list of elements

private void FeatureSelectionsFromTrace(UtilityNetwork utilityNetwork, TraceArgument traceArgument)
{
  // Get the trace manager from the utility network
  using (TraceManager traceManager = utilityNetwork.GetTraceManager())
  {
    UpstreamTracer tracer = traceManager.GetTracer<UpstreamTracer>();
    IReadOnlyList<Result> tracerResults = tracer.Trace(traceArgument);

    foreach (Result traceResult in tracerResults)
    {
      if (traceResult is ElementResult elementResult)
      {
        IReadOnlyList<Element> elements = elementResult.Elements;

        // Feature selection from a list of elements
        IReadOnlyList<Selection> selections = utilityNetwork.GetFeaturesForElements(elements);
      }
    }
  }
}

Network Diagrams

Get the Diagram Manager

using (DiagramManager diagramManager = utilityNetwork.GetDiagramManager())
{
  // Todo - do something
}

Get Network Diagrams

using (DiagramManager diagramManager = utilityNetwork.GetDiagramManager())
{
  // get all the diagrams
  IReadOnlyList<NetworkDiagram> diagrams = diagramManager.GetNetworkDiagrams();

  // get a diagram by name
  NetworkDiagram diagram = diagramManager.GetNetworkDiagram(diagrameName);

  // get diagrams by extent
  diagrams = diagramManager.GetNetworkDiagrams(extentOfInterest);

  // get diagrams from a set of utility network feature GlobalIDs
  diagrams = diagramManager.GetNetworkDiagrams(globalIDs);

  // get diagrams from a set of utility network feature GlobalIDs within an extent
  diagrams = diagramManager.GetNetworkDiagrams(extentOfInterest, globalIDs);
}

Get a list of Network Diagrams with inconsistent ConsistencyState

public List<NetworkDiagram> GetInconsistentDiagrams(UtilityNetwork utilityNetwork)
{
  // Get the DiagramManager from the utility network

  using (DiagramManager diagramManager = utilityNetwork.GetDiagramManager())
  {
    List<NetworkDiagram> myList = new List<NetworkDiagram>();

    // Loop through the network diagrams in the diagram manager

    foreach (NetworkDiagram diagram in diagramManager.GetNetworkDiagrams())
    {
      NetworkDiagramInfo diagramInfo = diagram.GetDiagramInfo();

      // If the diagram is not a system diagram and is in an inconsistent state, add it to our list

      if (!diagramInfo.IsSystem && diagram.GetConsistencyState() != NetworkDiagramConsistencyState.DiagramIsConsistent)
      {
        myList.Add(diagram);
      }
      else
      {
        diagram.Dispose(); // If we are not returning it we need to Dispose it
      }
    }

    return myList;
  }
}

Open a diagram pane from a Network Diagram

// Create a diagram layer from a NetworkDiagram (myDiagram)
DiagramLayer diagramLayer = await QueuedTask.Run<DiagramLayer>(() =>
{
  // Create the diagram map
  var newMap = MapFactory.Instance.CreateMap(myDiagram.Name, ArcGIS.Core.CIM.MapType.NetworkDiagram, MapViewingMode.Map);
  if (newMap == null)
    return null;

  // Open the diagram map
  var mapPane = ArcGIS.Desktop.Core.ProApp.Panes.CreateMapPaneAsync(newMap, MapViewingMode.Map);
  if (mapPane == null)
    return null;

  //Add the diagram to the map
  return newMap.AddDiagramLayer(myDiagram);
});

Get Diagram from DiagramLayer

public void GetDiagram(DiagramLayer diagramLayer)
{
  // note - methods need to run on MCT

  NetworkDiagram diagram = diagramLayer.GetNetworkDiagram();

  // get the consistency state from the layer
  DiagramLayerConsistencyState dlState = diagramLayer.ConsistencyState;

  // or from the diagram
  NetworkDiagramConsistencyState ndState = diagram.GetConsistencyState();
}

Get Diagram Templates

public void RetrieveDiagramTemplates(UtilityNetwork utilityNetwork)
{
  using (DiagramManager diagramManager = utilityNetwork.GetDiagramManager())
  {
    // get all templates
    IReadOnlyList<DiagramTemplate> templates = diagramManager.GetDiagramTemplates();

    // get a template by name
    DiagramTemplate template = diagramManager.GetDiagramTemplate(templateName);
  }
}

Get Network Diagrams from a Diagram Template

public void GetNetworkDiagramFromDiagramTemplates(UtilityNetwork utilityNetwork)
{
  using (DiagramManager diagramManager = utilityNetwork.GetDiagramManager())
  {
    // get the first templates
    DiagramTemplate template = diagramManager.GetDiagramTemplates().FirstOrDefault();

    // get the network diagrams fromt he template
    IEnumerable<NetworkDiagram> diagrams = template.GetNetworkDiagrams();

    // or get a network diagram by name
    NetworkDiagram diagram = template.GetNetworkDiagram(diagrameName);
  }
}

Create a Network Diagram

public void CreateNetworkDiagram(UtilityNetwork utilityNetwork, IEnumerable<Guid> globalIDs)
{
  using (DiagramManager diagramManager = utilityNetwork.GetDiagramManager())
  {
    // get the template
    DiagramTemplate template = diagramManager.GetDiagramTemplate(templateName);

    // create the diagram
    NetworkDiagram diagram = diagramManager.CreateNetworkDiagram(template, globalIDs);
  }
}

Get Network Diagram Information as JSON string

public void GetDiagramContent(UtilityNetwork utilityNetwork)
{
  using (DiagramManager diagramManager = utilityNetwork.GetDiagramManager())
  {
    // get a diagram by name
    NetworkDiagram diagram = diagramManager.GetNetworkDiagram(templateName);

    string json_content = diagram.GetContent(true, true, true, true);
  }
}

Get Diagram Elements

public void GetDiagramElements(MapView mapView, NetworkDiagram networkDiagram)
{
  // Create a DiagramElementQueryByExtent to retrieve diagram element junctions whose extent
  // intersects the active map extent

  DiagramElementQueryByExtent elementQuery = new DiagramElementQueryByExtent();
  elementQuery.ExtentOfInterest = MapView.Active.Extent;
  elementQuery.AddContents = false;
  elementQuery.QueryDiagramJunctionElement = true;
  elementQuery.QueryDiagramEdgeElement = false;
  elementQuery.QueryDiagramContainerElement = false;

  // Use this DiagramElementQueryByExtent as an argument to the QueryDiagramElements method
  DiagramElementQueryResult result = networkDiagram.QueryDiagramElements(elementQuery);

  // get the container, junction, edge elements
  //    in this case result.DiagramJunctionElements and result.DiagramEdgeElements will be empty 
  //    since elementQuery.QueryDiagramEdgeElement and elementQuery.QueryDiagramContainerElement are set to false
  IReadOnlyList<DiagramContainerElement> containerElements = result.DiagramContainerElements;

  IReadOnlyList<DiagramJunctionElement> junctionElements = result.DiagramJunctionElements;

  IReadOnlyList<DiagramEdgeElement> edgeElements = result.DiagramEdgeElements;
}

Get Diagram Aggregations

public void GetDiagramAggregation(NetworkDiagram networkDiagram)
{
  IReadOnlyList<DiagramAggregation> aggregations = networkDiagram.GetAggregations();
  foreach (var aggregation in aggregations)
  {
    var type = aggregation.AggregationType;
  }
}

Find Diagram Features for a set of utility network rows

public void FindDiagramFeatures(NetworkDiagram diagram, List<Guid> globalIDs)
{
  FindDiagramFeatureQuery featureQuery = new FindDiagramFeatureQuery();
  featureQuery.NetworkRowGlobalIDs = globalIDs;
  featureQuery.AddAggregations = true;
  featureQuery.AddConnectivityAssociations = true;
  featureQuery.AddStructuralAttachments = true;

  IReadOnlyList<FindResultItem> features = diagram.FindDiagramFeatures(featureQuery);
  foreach (var findFeature in features)
  {
    long objectID = findFeature.ObjectID;
    Guid guid = findFeature.GlobalID;
    GeometryType geometryType = findFeature.GeometryType;
    int sourceID = findFeature.SourceID;
  }
}

Find Utility Network Rows for a set of diagram features

public void FindDiagramRows(NetworkDiagram diagram, List<Guid> globalIDs)
{
  FindNetworkRowQuery rowQuery = new FindNetworkRowQuery();
  rowQuery.DiagramFeatureGlobalIDs = globalIDs;
  rowQuery.AddAggregations = true;

  IReadOnlyList<FindResultItem> rows = diagram.FindNetworkRows(rowQuery);
  foreach (var findRow in rows)
  {
    long objectID = findRow.ObjectID;
    Guid guid = findRow.GlobalID;
    GeometryType geometryType = findRow.GeometryType;
    int sourceID = findRow.SourceID;
  }
}

Find Initial Network Rows Used to create a Network Diagram

public void FindInitialNetworkRows(NetworkDiagram diagram)
{
  IReadOnlyList<FindResultItem> rows = diagram.FindInitialNetworkRows();
  foreach (var findRow in rows)
  {
    long objectID = findRow.ObjectID;
    Guid guid = findRow.GlobalID;
    GeometryType geometryType = findRow.GeometryType;
    int sourceID = findRow.SourceID;
  }
}

Change the Layout of a Network Diagram

public void DiagramElementQueryResultAndNetworkDiagramSubsetClasses(Geodatabase geodatabase, DiagramManager diagramManager, string diagramName)
{
  // Retrieve a diagram
  using (NetworkDiagram diagramTest = diagramManager.GetNetworkDiagram(diagramName))
  {
    // Create a DiagramElementQueryByElementTypes query object to get the diagram elements we want to work with
    DiagramElementQueryByElementTypes query = new DiagramElementQueryByElementTypes();
    query.QueryDiagramJunctionElement = true;
    query.QueryDiagramEdgeElement = true;
    query.QueryDiagramContainerElement = true;

    // Retrieve those diagram elements
    DiagramElementQueryResult elements = diagramTest.QueryDiagramElements(query);

    // Create a NetworkDiagramSubset object to edit this set of diagram elements
    NetworkDiagramSubset subset = new NetworkDiagramSubset();
    subset.DiagramJunctionElements = elements.DiagramJunctionElements;
    subset.DiagramEdgeElements = elements.DiagramEdgeElements;
    subset.DiagramContainerElements = elements.DiagramContainerElements;

    // Edit the shapes of the diagram elements - left as an exercise for the student
    TranslateDiagramElements(subset);

    // Save the new layout of the diagram elements
    diagramTest.SaveLayout(subset, true);
  }
}

Editing Network Diagram

public void EditDiagram(NetworkDiagram diagram, List<Guid> globalIDs)
{
  //     These routines generate their own editing transaction, and therefore cannot be wrapped
  //     in a separate transaction. Because the editing performed by these routines cannot
  //     be undone, thise routines can also not be called within an editing session. All
  //     edits in the current edit session must be saved or discarded before calling these
  //     routines.

  // refresh the diagram - synchronizes it based on the latest network topology
  diagram.Update();

  // append features to the diagram
  diagram.Append(globalIDs);

  // overite the diagram with a set of features 
  diagram.Overwrite(globalIDs);

  NetworkDiagramInfo info = diagram.GetDiagramInfo();
  if (info.CanExtend)
  {
    diagram.Extend(NetworkDiagramExtendType.ExtendByContainment);

    // or extend for only a set of utility network globalIDs
    diagram.Extend(NetworkDiagramExtendType.ExtendByContainment, globalIDs);
  }
  // delete a diagran
  diagram.Delete();
}
⚠️ **GitHub.com Fallback** ⚠️