Select to view content in your preferred language

How to create a Attribute Rule to populate a field based on its location inside a polygon?

2165
8
Jump to solution
09-21-2021 12:00 PM
Lownbey
Emerging Contributor

Is it possible to have a polygon set as a boundary to define a specific value in a field based on which boundary it was created within?

Currently there are two different boundaries in two different locations. 

i.e., Area 1 is called Forest, Area 2 is called City. If anything is created in the Forest area the location id will be populated with Forest, and if anything is created in the City area it will be populated with City.

The polygon boundary is one layer by its self.

The others will be a variety of different layers and consist of points, lines, polygons.

Can this be created into a attribute rule?

0 Kudos
1 Solution

Accepted Solutions
JohannesLindner
MVP Frequent Contributor

Sure, that's one of the most common use cases for Attribute Rules.

// Calculation Attribute Rule
// Field: LocationID
// Triggers: Insert, Update
// Exclude: True

// load the Areas poylgon class
var areas = FeatureSetByName($datastore, "NameOfFeatureclass", ["NameField"], true)

// intersect the areas with the inserted/edited feature
var inter = Intersects(areas, $feature)

// if feature is not in any area, return null
if(inter == null || Count(inter) == 0) {
  return null
}

// else return the first intersecting area's name field
return First(inter).NameField

Have a great day!
Johannes

View solution in original post

8 Replies
JohannesLindner
MVP Frequent Contributor

Sure, that's one of the most common use cases for Attribute Rules.

// Calculation Attribute Rule
// Field: LocationID
// Triggers: Insert, Update
// Exclude: True

// load the Areas poylgon class
var areas = FeatureSetByName($datastore, "NameOfFeatureclass", ["NameField"], true)

// intersect the areas with the inserted/edited feature
var inter = Intersects(areas, $feature)

// if feature is not in any area, return null
if(inter == null || Count(inter) == 0) {
  return null
}

// else return the first intersecting area's name field
return First(inter).NameField

Have a great day!
Johannes
Jaime_Carlino
Occasional Contributor

Hi Johannes,

Is there a way to modify this if I wanted multiple land cover classes (converted to polygons) to be populated into a field rather than just one area (forest vs city) in this example? I have 9 total land covers that I converted to polygons in their own feature class, and I'd like to populate a field in a parcel feature class that would include all of the land covers included within each parcel. Thank you! 

0 Kudos
JohannesLindner
MVP Frequent Contributor

If your land covers are all in the same feature class (untested):

var covers = FeaturesetByName(...)
var i_covers = Intersects(covers, $feature)

var out = []
for(var c in i_covers) {
    Push(out, c.LandCoverField)
}

return Concatenate(Distinct(out), ", ")

 

If you have each land cover class in its own feature class (untested):

var covers = {
    "Forest": FeaturesetByName(...),
    "Agriculture": FeaturesetByName(...),
    "Water": FeaturesetByName(...),
    }

var out = []
for(var cover in covers) {
    var i_cover = First(Intersects(covers[cover], $feature))
    if(i_cover != null) {
        Push(out, cover)
    }
}

return Concatenate(out, ", ")

 


Have a great day!
Johannes
0 Kudos
Jaime_Carlino
Occasional Contributor

Johannes, 

The first group of code in your recent response worked just as I needed it to. Thank you so much for your help!

0 Kudos
Lownbey
Emerging Contributor

Johannes,

Thank you for your help!

I have yet another questions. I have about 200 layers I need to add this to. What is the fastest or simplest way to do so?

 

0 Kudos
JohannesLindner
MVP Frequent Contributor

Probably Python.

In ArcGIS Pro, open the Python window:

JohannesLindner_0-1632384073958.png

 

Edit and run this code:

# characteristics of the attribute rule
rule_name = "LocationID"
rule_type = "CALCULATION"
triggers = "INSERT;UPDATE"
field = "LocationID"
is_editable = True
exclude = True
description = "Checks whether the feature is in a Forest or City area and inputs the corresponding value."
arcade_expression = """// Calculation Attribute Rule
// Field: LocationID
// Triggers: Insert, Update
// Exclude: True

// load the Areas poylgon class
var areas = FeatureSetByName($datastore, "NameOfFeatureclass", ["NameField"], true)

// intersect the areas with the inserted/edited feature
var inter = Intersects(areas, $feature)

// if feature is not in any area, return null
if(inter == null || Count(inter) == 0) {
  return null
}

// else return the first intersecting area's name field
return First(inter).NameField"""


# list of your layer names / feature class paths, must be connected as data owner to have the permission to add rules
layer_list = [
    "layer_1",
    "database.sde/featureclass_2",
    "layer_3",
    ]


for layer in layer_list:
    print(layer)
    try:
        arcpy.management.AddAttributeRule(
            in_table=layer,
            name=rule_name,
            type=rule_type,  
            triggering_events=triggers, 
            field=field,  
            is_editable=is_editable, 
            script_expression=arcade_expression,
            exclude_from_client_evaluation=exclude,
            description=self.description
            )
        print("success")
    except Exception as e:
        print("ERROR: {}".format(e))
        

Have a great day!
Johannes
0 Kudos
Lownbey
Emerging Contributor

Johannes,

I appreciate it, however, there is not an easier way to apply the rules besides typing 200 layers in a list?

 

Is there not a way on the server or api to automatically load x rules on y layers?

0 Kudos
JohannesLindner
MVP Frequent Contributor

None that I know of (I know very little of the server stuff and of the python API). Maybe if you ask that as a new question, someone can tell you for sure.


Have a great day!
Johannes
0 Kudos