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.
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
//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
//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
//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
//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
//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'
}
]
}
]
}
Article Discussion: