With ArcGIS Pro 2.4 and Enterprise 10.7.1, we have introduced the ability to author geodatabase attribute rules that edit features on other classes using special dictionary keywords and syntax. This blog will provide an overview of this functionality with examples on how this can be configured. To learn more on the topic, see Attribute rule dictionary keywords in the help. All Arcade scripts and examples are available as a file geodatabase in this blog for you to download and work with here.
Create new features on another class
In this example we have two feature classes: `pointClass` and `polygonClass`. We want to create an attribute rule that accomplishes three goals:
- Buffer each point feature created in the pointClass by 40 feet to create a circle polygon.
- Insert the polygon buffer geometry into the polygonClass.
- Maintain relation between the two classes through creation of a pointGuid field in the polygonClass which points to the Global ID of the pointClass.
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.
//The attribute rule is added to the pointClass on the Field field on insert
//buffer the point feature 40 feet, this will give us a circle polygon geometry
var bufferedFeature = buffer($feature,40)
return {
//we want to just return the value of field `Field` no change require
"result": $feature.field,
//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" : "polygonclass",
//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 pointGuid with the globalId of the feature being created
"attributes":
{
"pointGuid": $feature.globalid
},
//we want to use the buffered geometry to insert the polygon
"geometry": bufferedFeature
}
]
}
]
}
Note: The `result` key is used to return the value to persist in the field `Field` which the attribute rule is assigned to. In this case I really didn't do anything to the feature being created so I just returned the same value of the field `Field` (I know, terrible name for a field sorry). When returning the same value, the system will detect that there is no change and will not perform an additional edit to the feature. This is useful to avoid triggering behavior associated with the field (if any) such as extension specific behavior, e.g. dirty area management in utility network.
Update existing features in another class
Now we want to add another attribute rule that performs an update. With this rule we want to ensure that the buffered polygon moves along with the point feature when it is moved. The best solution to achieve this would be to update the buffered polygon feature geometry with a new buffer after the update.
//The attribute rule is added to the pointClass on the Field field on update
//buffer the point feature 40 feet, this will give us a circle polygon geometry
var bufferedGeometry = buffer($feature,40)
//get the guid of the point feature being updated
var globalId = $feature.globalid
//find the buffered geometry feature
var fs = FeatureSetbyName($datastore, "polygonclass")
//by filtering on the pointGuid
var bf = filter(fs, "pointGuid = @globalId")
//if we couldn't find it exit (someone might've deleted it)
if (count(bf) == 0) return $feature.field;
//get the feature row (we should only have one )
var bufferedFeature = first(bf)
return {
//we want to just return the value of field `Field` no change require
"result": $feature.field,
//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" : "polygonclass",
//the type of edit, in this case we want to update so we say `updates`, its an array since we can make many updates
"updates" : [
{
//what feature we need to update? we can either find it by the globalid or the objectId
"globalID" : bufferedFeature.globalId,
//what do we want to update (we can optionally add attributes property and update properties there)
"geometry": bufferedGeometry
}
]
}
]
}
Note: This attribute rule will be executed for any update on the point feature. That means we will be updating the geometry of the buffered feature for every update to the point feature and this could be unnecessarily expensive. In ArcGIS Pro 2.5 we will introduce the concept of $originalfeature in Arcade which will allow us to compare the state of the feature before it was edited so we only proceed with the edit if the geometry has change. A following blog will be written detailing this capability.
Delete existing features on another class
Finally, we want to create an attribute rule that will delete other features. We want to use the same example used above so that the buffered geometry feature is removed from the polygonClass if the corresponding point feature is removed from pointClass.
//The attribute rule is added to the pointClass on the Field field executed on delete
//buffer the point feature 40 feet, this will give us a circle polygon geometry
var bufferedGeometry = buffer($feature,40)
//get the guid of the point feature being updated
var globalId = $feature.globalid
//find the buffered geometry feature
var fs = FeatureSetbyName($datastore, "polygonclass")
//by filtering on the pointGuid
var bf = filter(fs, "pointGuid = @globalId")
//if we couldn't find it exit (someone might've deleted it)
if (count(bf) == 0) return $feature.field;
//get the feature row (we should only have one )
var bufferedFeature = first(bf)
return {
//we want to just return the value of field `Field` no change require
"result": $feature.field,
//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" : "polygonClass",
//the type of edit, in this case we want to delete so we say `deletes`, its an array since we can make many deletes
"deletes" : [
{
//what feature we need to delete?
"globalID" : bufferedFeature.globalId
}
]
}
]
}
In this blog we outlined the building blocks that can enable you to build interesting and powerful Arcade expressions. With great power comes great creativity. Go forth and write cool attribute rules!
You can download the file geodatabase with the examples discussed in this blog here.
Edit: 1/30/2020
In some examples above, I call Count(fs) to check if the featureset is empty. If it is not empty I call First(fs) to get the first row. Bear in mind that these are two queries to the database and to make attribute rule more performant a better pattern is to call in First(fs) and guard against null instead.
Article Discussion: