Select to view content in your preferred language

Arcade Expression: Return value based on area or length of overlap

1114
4
Jump to solution
07-14-2023 07:22 AM
ChelseaP
New Contributor II

Below is the arcade expression I'm using to intersect my field maps collection layers with the UTM grid layer in order to return the zone value in a field. This works fine for points but in the case for lines and polygons they might span two UTM zones. I'm trying to develop a way to deal with this. I was thinking I could tweak this expression in order to return the value of the grid polygon the line or polygon overlaps with the most. Wondering if anyone knows how to do that? Would I have to get the length or area first then return the value based on that number? 

If anyone has an easier solution please let me know. 

Arcade Expression:

var UTMgrid = Intersects(FeatureSetByName($map, "UTM Zone Grid"), $feature)
var zone = false;
for (var g in UTMgrid){
  zone=true;
}
if (zone==true){
  return Text(g.Zone);
}
else{
  return "No Data";
}
0 Kudos
1 Solution

Accepted Solutions
jcarlson
MVP Esteemed Contributor

Note: there's really no reason to have the two portions separate, but I split it out for explanatory purposes. If you wanted, you could get the comparison section folded right into the for (var g...) loop:

// variables for overlap comparison
var biggest_overlap = '';
var overlap_amount = 0;

// intersection
var UTMgrid = Intersects(FeatureSetByName($map, "UTM Zone Grid"), $feature)

// check if any features in the intersection
if (Count(UTMgrid) > 0) {
  // iterate over zones, calculate intersection amount and compare
  for (var g in UTMgrid) {
    var xs = Intersection($feature, g)

    // get area of overlap; subsitute length if linear features
    var xs_overlap = Area(xs)

    if (xs_overlap > overlap_amount) {
      overlap_amount = xs_overlap
      biggest_overlap = Text(g['Zone'])
    }
  }
} else {
  return "No Data";
}

return biggest_overlap
- Josh Carlson
Kendall County GIS

View solution in original post

0 Kudos
4 Replies
jcarlson
MVP Esteemed Contributor

If you iterate over a FeatureSet, you should always code on the assumption that multiple features may be returned. I would create an array and push the results into it.

// intersection
var UTMgrid = Intersects(FeatureSetByName($map, "UTM Zone Grid"), $feature)

// array to hold zone information
var zones = [];

// check if any features in the intersection
if (Count(UTMgrid) > 0) {
  // iterate over zones, populate array
  for (var g in UTMgrid) {
    Push(zones, g)
  }
} else {
  return "No Data";
}

Once we get to this point, assuming there were intersecting features found, we'll have an array of one or more features in our zones array.

Now, to determine which overlap is bigger, we can use the function Intersection. This will give us a geometry that we can measure and compare. You could create an array of dicts and use a custom sorting function to compare them, but we'll try a simpler approach.

// variables for overlap comparison
var biggest_overlap = '';
var overlap_amount = 0;

for (var z in zones) {
  var xs = Intersection($feature, zones[z])
  
  // get area of overlap; subsitute length if linear features
  var xs_overlap = Area(xs)
  
  // check against variables, replace of greater
  if (xs_overlap > overlap_amount) {
    overlap_amount = xs_overlap
    biggest_overlap = zones[z]['Zone']
  }
}

return Text(biggest_overlap)

 

 

- Josh Carlson
Kendall County GIS
0 Kudos
jcarlson
MVP Esteemed Contributor

Note: there's really no reason to have the two portions separate, but I split it out for explanatory purposes. If you wanted, you could get the comparison section folded right into the for (var g...) loop:

// variables for overlap comparison
var biggest_overlap = '';
var overlap_amount = 0;

// intersection
var UTMgrid = Intersects(FeatureSetByName($map, "UTM Zone Grid"), $feature)

// check if any features in the intersection
if (Count(UTMgrid) > 0) {
  // iterate over zones, calculate intersection amount and compare
  for (var g in UTMgrid) {
    var xs = Intersection($feature, g)

    // get area of overlap; subsitute length if linear features
    var xs_overlap = Area(xs)

    if (xs_overlap > overlap_amount) {
      overlap_amount = xs_overlap
      biggest_overlap = Text(g['Zone'])
    }
  }
} else {
  return "No Data";
}

return biggest_overlap
- Josh Carlson
Kendall County GIS
0 Kudos
ChelseaP
New Contributor II

Wow thanks! This works exactly how I hoped it would!

0 Kudos
SFM_TravisBott
Occasional Contributor III

@jcarlson Would you have any suggestions on how to adapt this to compare what would effectively be the null part of the geometry for the non-intersecting part of feature to the intersecting part? I'm attempting this same operation on a dataset that is not wall-to-wall. The logic to handle non-intersection works fine and returns the desired value. The problem is even if a tiny part of the feature intersects a zone it brings back the value for that zone.

What I would like is, if the area of non-intersection is greater than the area intersected, return the 'null' value for non-intersection. My attempt is below, where I was thinking to define it as 'if there is no intersection or if the intersected area constitutes less than half of the total area of the feature':

 

var overlap_amount = 0

var cdfPortal = Portal("*")
var fsFHSZ = FeatureSetByPortalItem(cdfPortal, "*", 0, ['FHSZ'])

var fsFHSZIntersect = Intersects(fsFHSZ, $feature)
var areaFeature = Area($feature)
var areaFHSZIntersect = Area(fsFHSZIntersect)

if (IsEmpty(fsFHSZIntersect) || areaFHSZIntersect < areaFeature/2) {
    return 0
} else {
   for(var z in fsFHSZIntersect) {
    var zone = Intersection($feature, z)
    var zoneOverlap = Area(zone)
    if (zoneOverlap > overlap_amount) {
       overlap_amount = zoneOverlap
       var largest_overlap = z["FHSZ"]
       return largest_overlap 
    }
   }
}

 

 

A direct adaptation of your code has the same problem, as does the function/dictionary technique that I've attempted in a few variations:

 

var cdfPortal = Portal("*")
var fsFHSZ = FeatureSetByPortalItem(cdfPortal, "*", 0, ['FHSZ'])

var fsFHSZIntersect = Intersects(fsFHSZ, $feature)
var intersected_zones = []
for(var z in fsFHSZIntersect) {
    var zone = {
        Zone: z.FHSZ,
        Overlap: Area(Intersection(z, $feature))
    }
    Push(intersected_zones, zone)
}

function sort_by_overlap(z1, z2) { return z1.Overlap < z2.Overlap }

var zone = First(Sort(intersected_zones, sort_by_overlap))

return zone.Zone

if (Zone == null) {
     return 0
 } else {
     return zone.Zone
 }

 

 

In the below image the result should be 0, but instead it brings up 'Moderate' (the area in yellow which it intersects slightly). 

SFM_TravisBott_0-1697836483138.png

 

 

 

 

0 Kudos