ArcGIS Utility Network

Advanced Attribute Rules - Creating Utility Network Associations with attribute Rules

With ArcGIS Pro 2.4 and Enterprise 10.7.1, we introduced the ability to author geodatabase attribute rules that edit features on other classes using special dictionary keywords and syntax. With those releases I authored a blog post that demonstrates how to edit other classes using attribute rules. At ArcGIS Pro 2.5 and Enterprise 10.8, we have made improvements that allow me to demonstrate the use of dictionary keywords to create different types of utility network associations with attribute rules. With these improvements, we recommend using 2.5/10.8 to get the most out of working with attribute rules and associations (note that some of the examples provided in this post will not work at 2.4/10.7.1.) To learn more on the topic, see Attribute rule dictionary keywords in the help.

 

Note: This blog uses Naperville Electric utility network data model.

 

Creating a feature attaches itself to a structure within 50 feet

In this example, we want that every time we create a Medium Voltage Attachment junction feature, which is a subtype on the Electric Junction class, to attach it to the first nearby Medium Voltage Single Pole we find within 50 feet.

 

We will create a new immediate calculation attribute rule using the following expression with a triggering event on insert. Make sure to exclude evaluation on the client by checking the option Exclude from application evaluation.

an Attribute rule that attaches devices to the nearest pole
an Attribute rule that attaches devices to the nearest pole

Using the following expression, when creating an overhead attachment within 50 feet of a pole the attachment will automatically attaches to the pole

//$feature is an ElectricJunction Medium Voltage Overhead Attachment
//Get the structureJunction feature set 
var sjFeatureSet = FeatureSetByname($datastore, "StructureJunction", ["objectid"] ,false)
//We are only interested in Single Pole - Electric Medium Voltage Poles
// Asset Group Electric Medium Voltage Poles - code 121
// Asset Type Single Pole - 324
var polesFeatureSet = Filter(sjFeatureSet, "ASSETGROUP = 121 AND ASSETTYPE = 324")
//buffer the $feature which is the Medium Voltage overhead attachment by 50 feet
var g = buffer($feature, 50)
//interset and find any poles within 50 feet  
var intersectedPoles = Intersects(polesFeatureSet,g)
//make sure we have some results, if not just return
if (count(intersectedPoles) == 0) 
  return $feature.notes;
//get the first pole we find
var pole = first(intersectedPoles)
//return the dictionary
return {
         //we want to just return the value of field `notes` no change require
         "result": $feature.notes,
         //this keyword indicates an edit that need to happen, its an array since we can make many edits
         "edit": 
              [
                {
                 //the other class we want to edit, the pole
                 "className": "structurejunction",
                //the type of edit, in this case `updates` are always used when modifying associations, its an array since we can make many updates
                 "updates":
                 [
                   {
                   //we want to find what pole to associate with, we can either use the objectId or the globalId
                   "objectID": pole.objectid,
                   //I want the pole feature to be my 'structure' ($feature the junction)
                   "associationType": 'structure'
                   }
                 ]
                }
              ]
            }
  

The script can also be modified slightly if necessary to fail the edit if no nearby poles were found


//make sure we have some results, 
if (count(intersectedPoles) == 0)   return {"errorMessage": "Overhead Medium Voltage attachment must be created within 50 feet of a Single Pole - Electric Medium Voltage Pole"}

Creating a structure attaches all nearby applicable features to itself

Now we want to create a medium voltage single pole and attaches any nearby Medium Voltage Attachments to it. However we want to make sure those junctions are not already attached to some other poles so we will filter for that. Without that additional filter we will get an error when we try to attach a junction that is already attached to another pole.

 

We will create a calculation rule on the StructureJunction class this time as follows

Attribute Rules attaches all nearby devices to the pole
Attribute Rules attaches all nearby devices to the pole


//creating pole  
//$feature is the StructureJunction (pole)
//Get the ElectricJunction feature set 
var ejFeatureSet = FeatureSetByname($datastore, "ElectricJunction", ["objectid"] ,false)
//We are only interested in Medium Voltage Attachments of 
// Asset Group Medium Voltage Attachment - code 7
// Asset Type Overhead - 60
// Also we only want junctions that are not already associated with anything. 
var mvaFeatureSet = Filter(ejFeatureSet, "ASSETGROUP = 7 AND ASSETTYPE = 60 AND ASSOCIATIONSTATUS = 0 ")
//buffer the pole
var g = buffer($feature, 50)
//Find all interested junctions
var interestedJunctions = Intersects(mvaFeatureSet,g)
//if there isn't any thats ok just exit
if (count(interestedJunctions) == 0) return $feature.notes
//create an array of junctions to be returned and attached
var updatedJunctions = []
var jCount = 0;
for (var f in interestedJunctions){
    updatedJunctions[jCount++] = {
             //the objectId of the junction
             "objectID": f.objectid,
             //we want the junction to be attached to $feature   
             "associationType": 'attached'
            }
}

//return the dictionary
return {
         //we want to just return the value of field `notes` no change require
         "result": $feature.notes,
         //this keyword indicates an edit that need to happen, its an array since we can make many edits
         "edit": 
              [
                {
                 //the other class we want to edit
                 "className": "ElectricJunction",
                 //the list of updates we need to make
                 "updates":updatedJunctions
                 
                }
              ]
            }
 

Creating a feature associate itself as content with a nearby container

Now we want to create a medium voltage fuse and associate it with a nearby fuse bank as content. The logic is similar, we want to find fuse banks within 50 feet and pick the first feature and associate the created fuse to it.

 

We will create a calculation rule on the ElectricDevice class as follows

Attribute rule that makes devices content
Attribute rule that makes devices content


//creating a fuse contains itself to its assembly

//Assembly
//Asset Group: Medium Voltage Fuse Bank 14
//AssetType: Overhead Disconnect Fuse Holder 221
//Device
//Asset Group: Medium Votlage Fuse 29
//Asset Type: Overhead Cutout Sectionalizer 566

//$feature is an ElectricDevice Medium Voltage Fuse
//Get the ElecctricAssembly feature set 
var esFeatureSet = FeatureSetByname($datastore, "ElectricAssembly", ["objectid"] ,false)
//We are only interested in Medium Voltage Fuse Bank assemblies
// Asset Group Medium Voltage Fuse Bank Code 14
// Asset Type Overhead Disconnect Fuse Holder code 221
var assembliesFeatureSet = Filter(esFeatureSet, "ASSETGROUP = 14 AND ASSETTYPE = 221")
//buffer the $feature which is the Medium Voltage Fuse by 50 feet
var g = buffer($feature, 50)
//interset and find any fuse banks within 50 feet  
var intersectedAssemblies = Intersects(assembliesFeatureSet,g)
//make sure we have some results, if not just return
if (count(intersectedAssemblies) == 0) 
  return $feature.notes;

//get the first assembly we find
var assembly = first(intersectedAssemblies)
//return the dictionary
return {
         //we want to just return the value of field `notes` no change require
         "result": $feature.notes,
         //this keyword indicates an edit that need to happen, its an array since we can make many edits
         "edit": 
              [
                {
                 //the other class we want to edit, the assembly
                 "className": "ElectricAssembly",
                //the type of edit, in this case `updates` are always used when modifying associations, its an array since we can make many updates
                 "updates":
                 [
                   {
                   //we want to find what assembly to associate with, we can either use the objectId or the globalId
                   "objectID": assembly.objectid,
                   //I want the assembly feature to be the $feature's 'container' ($feature is the fuse device)
                   "associationType": 'container'
                   }
                 ]
                }
              ]
            }

Creating a container feature associate all nearby features as content to it.

We want to create a fuse bank and associate it all nearby fuses as content. This time we want to make sure that the nearby fuses are not already contained to any other containers.

 

We will create a calculation rule on the ElectricAssembly class as follows

Attribute rules creates containment association
Attribute rules creates containment association


//creating assembly  
//$feature is the ElectricAssembly (fuse bank)
//Get the ElectricDevice featureset 
var edFeatureSet = FeatureSetByname($datastore, "ElectricDevice", ["objectid"] ,false)
// We are only interested in Medium Voltage Fuse of type Overhead Cutout Sectionalizer
//Device
//Asset Group: Medium Votlage Fuse 29
//Asset Type: Overhead Cutout Sectionalizer 566
// Also we only want devices that are not already associated with anything. 
var mvfFeatureSet = Filter(edFeatureSet, "ASSETGROUP = 29 AND ASSETTYPE = 566 AND ASSOCIATIONSTATUS = 0 ")
//buffer the assembly
var g = buffer($feature, 50)
//Find all interesected devices
var intersectedDevices = Intersects(mvfFeatureSet,g)
//if there isn't any thats ok just exit
if (count(intersectedDevices) == 0) return $feature.notes
//create an array of junctions to be returned and attached
var updatedDevices = []
var dCount = 0;
for (var f in intersectedDevices){
  updatedDevices[dCount++] = {
             //the objectId of the junction
             "objectID": f.objectid,
             //we want the device to be content to $feature assembly
             "associationType": 'content'
            }
}

//return the dictionary
return {
         //we want to just return the value of field `notes` no change require
         "result": $feature.notes,
         //this keyword indicates an edit that need to happen, its an array since we can make many edits
         "edit": 
              [
                {
                 //the other class we want to edit
                 "className": "ElectricDevice",
                 //the list of updates we need to make
                 "updates":updatedDevices
                 
                }
              ]
            }

Creating a feature creates a connectivity association with another feature

In this example, we want when we create a Low Voltage Line End to automatically creates a connectivity association to the Low Side terminal of the first Transformer it finds.

 

We will create a calculation rule on the ElectricJunction class as follows

Attribute Rules connectivity associations
Attribute Rules connectivity associations


 //creating junction  
//$feature is the ElectricJunction (Low Voltage Line End)
//Get the ElectricDevice featureset 
var edFeatureSet = FeatureSetByname($datastore, "ElectricDevice", ["objectid"] ,false)
//buffer the junction
var g =  Buffer($feature,50)
//We are only interested in transformers 
//Asset Group: Medium Voltage Transformer 38
//Asset Type: Overhead Three Phase 788
var mvtFeatureSet = Filter(edFeatureSet, "ASSETGROUP = 38 AND ASSETTYPE = 788")
//find all intersected transformers
var intersetdTransformers = Intersects(mvtFeatureSet,g)
//if nothing just return
if (count(intersetdTransformers) == 0) return $feature.notes
//get the first transformer
var transformer = first(intersetdTransformers)
//return the dictionary
return {
         //we want to just return the value of field `notes` no change require
         "result": $feature.notes,
         //this keyword indicates an edit that need to happen, its an array since we can make many edits
         "edit": 
              [
                {
                 //the other class we want to edit, the Device
                 "className": "ElectricDevice",
                //the type of edit, in this case `updates` are always used when modifying associations, its an array since we can make many updates
                 "updates":
                 [
                   {
                   //we want to find what assembly to associate with, we can either use the objectId or the globalId
                   "objectID": transformer.objectid,
                   //since Transformer is a multiterminal device, we need to specify which terminal to connect to
                   "toTerminal" : "XFR:Low",
                   //I want the assembly feature to be the $feature's 'container' ($feature is the fuse device)
                   "associationType": 'connected'
                   }
                 ]
                }
              ]
            }
 

Creating a feature creates other features and associate them

So far in all our examples we were dealing with existing features to associate our $feature with. However, you can still use the normal dictionary to create other features and also associate them in the process. Here is an example

 

We want to write an attribute rule that triggers on inserting a new assembly. Every time we create a medium voltage regulator bank, the attribute rule will create 3 medium voltage arresters and also creates a containment association between the bank and the arresters. Here is how to do it.

 

We will create a calculation rule on the ElectricAssembly class as follows

 

Attribute Rules creates features & assocaitions
Attribute Rules creates features & assocaitions


//Creating a three phase Medium Voltage Regulator bank asembly will create 3 arrestors and contain them.
//This can also be achieved with stamped template in pro
// but the attribute rule version will work with any client since the AR is executd on the server


//buffer the point feature 40 feet, this will give us a circle polygon geometry 
var featureGeo = Geometry($feature)
//create the geometry of the 3 Arresters, offset the z so we don't get errors.
var a1Geo = Point({ 'x': featureGeo.x, 'y': featureGeo.y, 'z': 0, "spatialReference": featureGeo.spatialReference })
var a2Geo = Point({ 'x': featureGeo.x, 'y': featureGeo.y, 'z': 2, "spatialReference": featureGeo.spatialReference })
var a3Geo = Point({ 'x': featureGeo.x, 'y': featureGeo.y, 'z': 4, "spatialReference": featureGeo.spatialReference })


return {
       //we want to just return the field no change require
      "result": $feature.notes,
       //this keyword indicates an edit that need to happen, its an array since we can make many edits
       "edit": [  
           {  
               //the other class we want to edit
               "className" : "ElectricDevice", 
               //the type of edit, in this case we want to add so we say `adds`, its an array since we can make many inserts
               "adds" : [
                       
                      {
                            //the attribute of the feature we want to add, 
                            // we only want to populate the arrestor assetgroup and assettype these values are for Medium Voltage Arrester and MV Line Arrester
                            "attributes":  
                              {
                                   "assetgroup": 25 ,
                                   "assetType": 482
                              }, 
                            //we want to use the geometry to insert the arrester
                            "geometry": a1Geo ,

                            //I want the arrester point feature to be the $feature's 'content' ($feature is the Medium Voltage Regulator Bank assembly )
                            "associationType": 'content'

                      },
                      {
                        //the attribute of the feature we want to add, 
                        // we only want to populate the arrestor assetgroup and assettype these values are for Medium Voltage Arrester and MV Line Arrester
                        "attributes":  
                          {
                               "assetgroup": 25 ,
                               "assetType": 482
                          }, 
                        //we want to use the geometry to insert the arrester
                        "geometry": a2Geo ,

                        //I want the arrester point feature to be the $feature's 'content' ($feature is the Medium Voltage Regulator Bank assembly )
                        "associationType": 'content'

                  },
                  {
                    //the attribute of the feature we want to add, 
                    // we only want to populate the arrestor assetgroup and assettype these values are for Medium Voltage Arrester and MV Line Arrester
                    "attributes":  
                      {
                           "assetgroup": 25 ,
                           "assetType": 482
                      }, 
                    //we want to use the geometry to insert the arrester
                    "geometry": a3Geo ,

                    //I want the arrester point feature to be the $feature's 'content' ($feature is the Medium Voltage Regulator Bank assembly )
                    "associationType": 'content'

                 }


             ]


            }
      
     ]

}

About the author

Product Engineer at Esri, Author of several GIS books and Software Engineering Content Creator on YouTube and a podcast host.

Connect:
1 Comment
Oldest
Newest
Inline Feedbacks
View all comments

Next Article

What’s new in ArcGIS StoryMaps (April 2024)

Read this article