I am trying to add calculations for areas that intersect a project boundary, but have run into an issue with single part polygons returning an area for each intersecting polygon. I ran a test with a new layer with the polygons dissolved to multi part polygons and I get the results I want, but I need to be able to group the data from single part polygons as its not possible to edit some of the layers I need to run this on. I've included the Arcade code below that I've used, along with screen captures of the results on the single and multi part polygons.
I did some searching and found the GroupBy function, but cant figure out how to set that up it will even do what I want.
var MA_intersect = Intersects(FeatureSetByName($map,"ManagementAreas"), $feature);
var cnt = Count(MA_intersect);
if (cnt > 0) {
var MAs = "";
for (var MA in MA_intersect) {
var intersectArea = 0
intersectArea += Round(AreaGeodetic(Intersection($feature, MA), "acres"), 1);
MAs += TextFormatting.NewLine + MA.NAME + ": " + Text(intersectArea, '#,###.#') + " Acres" + TextFormatting.NewLine;
}
//result += TextFormatting.NewLine + MAs;
return MAs;
} else {
return "No Overlaping Management Areas"
}
Results with multi part polygons:
Results with single part polygons (I want this to look like the popup above):
Solved! Go to Solution.
Ah, that's right. Intersection will give the total area of the full shape of each intersected feature, not just the intersection area. I've no doubt those values are calculating accurately, but they're just more than you want.
To constrain the area calculation to just the overlapping area, we have to take another approach. We'll actually abandon the grouping, as this does not return geometry we can work with. Further complicating matters is the fact that the FeatureSet, being derived from a feature class, is immutable, otherwise we could just use SetGeometry on it.
Instead, we will iterate over our intersected features, calculate the area for the Intersection, then push those values to a separate dictionary. We can use HasKey to determine whether the calculated area is a new entry or simply added to an existing value. We'll then use that dictionary to cobble together the output string.
Be sure to use AreaGeodetic instead of just Area! And yes, this calculation will depend on the view scale rather than the internal area attribute, which is regrettable. But as we're rounding off to a single decimal place, that's probably okay. One benefit of this approach is that the units are specified in the calculation, so there's no conversion to manage.
var lu = FeatureSetByName($map,"Assessor Landuse", ['landuse', 'SHAPE__Area'])
var int_lu = Intersects($feature, lu)
var int_dict = {}
for(var i in int_lu){
var xs = AreaGeodetic(Intersection($feature, i), 'acres')
if(HasKey(int_dict, i)){
int_dict[i.landuse] += xs
} else {
int_dict[i.landuse] = xs
}
}
var out_str = 'Acreage by Landuse:'
for(var d in int_dict){
out_str += '\n' + d + '\t|\t' + Text(int_dict[d], '#,###.#')
}
return out_str
There are probably other ways to get that to format a bit more nicely in the output, but this should get you values that make a bit more sense.
Once you have your FeatureSet, GroupBy is definitely what you want. It can get more complicated for returning multiple fields and statistics, but you essentially just want a sum of the acreage by the type of management area, so it's more straightforward.
Here's part of an example using my county's landuse and parcel layers. For a given parcel of land, there are a number of different landuse types present.
var lu = FeatureSetByName($map,"Assessor Landuse", ['landuse', 'SHAPE__Area'])
var int_lu = Intersects($feature, lu)
var grp_dict = {
name: 'total_area',
expression: 'round(SHAPE__Area / 43560, 2)',
statistic: 'SUM'
}
GroupBy(int_lu, 'landuse', grp_dict)
This will return a FeatureSet, which by itself, is not useful for the popup text.
You already have the next part, though! Simply take the output FeatureSet from GroupBy and feed that into the for loop that constructs the output string.
Thanks Josh,
I've been trying to get the GroupBy to work and I think I'm close but there is still an issue with the calculated area. With the code below I'm getting for the popup. But the acres calculation is not accurate as its not adding up to the total acres in the project area. I pulled up the Shape_Area field and did an Arcade AreaGeodetic calculation on the project boundary to confirm, but can't figure out what the issue is. Our data is in a UTM projection which does explain the Shape_Area to Acres seen below, but if I use the square meters to acres conversion of 4,046.856 the calculation is still off. PVT should be about 30.7 acres
Shape_Area / 43560 =
Shape_Area / 4046.856 =
var MA_intersect = Intersects(FeatureSetByName($map,"ManagmentAreas"), $feature);
var grp = {
name: 'total_area',
expression: 'round(Shape__Area / 43560, 1)',
statistic: 'SUM'
}
var group = GroupBy (MA_intersect, ['NAME'], grp)
var cnt = Count(group);
if (cnt > 0) {
var MAs = "";
for (var MA in group) {
MAs += MA.NAME + ": " + Text(MA.total_area, '#,###.#') + " Acres" + TextFormatting.NewLine;
}
return MAs;
} else {
return "No Overlaping Management Areas"
}
Ah, that's right. Intersection will give the total area of the full shape of each intersected feature, not just the intersection area. I've no doubt those values are calculating accurately, but they're just more than you want.
To constrain the area calculation to just the overlapping area, we have to take another approach. We'll actually abandon the grouping, as this does not return geometry we can work with. Further complicating matters is the fact that the FeatureSet, being derived from a feature class, is immutable, otherwise we could just use SetGeometry on it.
Instead, we will iterate over our intersected features, calculate the area for the Intersection, then push those values to a separate dictionary. We can use HasKey to determine whether the calculated area is a new entry or simply added to an existing value. We'll then use that dictionary to cobble together the output string.
Be sure to use AreaGeodetic instead of just Area! And yes, this calculation will depend on the view scale rather than the internal area attribute, which is regrettable. But as we're rounding off to a single decimal place, that's probably okay. One benefit of this approach is that the units are specified in the calculation, so there's no conversion to manage.
var lu = FeatureSetByName($map,"Assessor Landuse", ['landuse', 'SHAPE__Area'])
var int_lu = Intersects($feature, lu)
var int_dict = {}
for(var i in int_lu){
var xs = AreaGeodetic(Intersection($feature, i), 'acres')
if(HasKey(int_dict, i)){
int_dict[i.landuse] += xs
} else {
int_dict[i.landuse] = xs
}
}
var out_str = 'Acreage by Landuse:'
for(var d in int_dict){
out_str += '\n' + d + '\t|\t' + Text(int_dict[d], '#,###.#')
}
return out_str
There are probably other ways to get that to format a bit more nicely in the output, but this should get you values that make a bit more sense.
Thanks Josh, that got me to what I needed, with just 1 change. I needed i.NAME in the HasKey part not just i. Without the i.NAME only seemed to work were I didn't have multiple polygons with the same management area within the project. My final code is below incase it helps anyone else.
var managementareas = FeatureSetByName($map,"ManagmentAreas");
var int_ma = Intersects($feature, managementareas)
var int_dict = {}
var xs = 0
for (var i in int_ma){
var xs = AreaGeodetic(Intersection($feature, i), 'acres')
if(HasKey(int_dict,i.Name)){
int_dict[i.NAME] += xs
} else {
int_dict[i.NAME] = xs
}
var out_str = 'Management Area Acreage: ';
for (var d in int_dict){
out_str += '\n' + d + '\t|\t' + Text(int_dict[d], '#,###.#')
}}
return out_str
Glad to hear it! Good catch on that typo, too! That was quite a process getting to the solution!