mmckeanesriaustralia-com-au-esridist

Creating Aggregated Lists from Overlapping Point and Polygon Features. A brief guide to creating lists in Arcade.

Blog Post created by mmckeanesriaustralia-com-au-esridist Employee on Oct 7, 2019


In this blog I will provide an example of how to leverage Arcade functionality to build an aggregated list of grouped attribute values and their counts within a Pop-up. Kelly Gerrow provided inspiration from her blog Overlapping Features in Pop-Ups Quick Introduction to Using FeatureSets with Arcade. Additionally, Paul Bakers blog series on Feature Sets is highly recommended reading; particularly for those that are new to FeatureSets. He also has several great Arcade examples.

 

For this example, I am using two authoritative layers from the City of Raleigh. The first layer is the Police District Boundaries for the city and the second details daily crime events that have occurred within the districts. Clicking through each individual crime point to determine its category and keeping track of the number in each district would be quite laborious. It would be much more intuitive to select the Police district polygon to view an aggregated list and count of the crime categories that occur within its boundaries.

 

 

Using Arcade, I was able to produce a single pop-up from the Police district polygon to display the aggregated list of crime categories and their counts (number within each district). This example is available to view in this Web Map.

 

 

The steps I took to produce this result are:

  1.      Select the Police district layer and then select configure Pop-ups and navigate to the Add Attribute Expressions section.

 

  1.       Create the Expression.     
    1.       Create your variables

                 

var listed = { };
var counter = 0;
var crime = " ";
var crimelist = " ";
var intersectpts = Intersects($feature, FeatureSetByName($map,"Daily Raleigh Police Incidents"));

 

               The first four variables allow for the use of a dictionary to add the crime categories and their counts. As I will                explain shortly the expression will loop through the feature and create a record for each crime type. If it already                exists, it will add one to the current count. If it doesn’t have the value listed, it will add it.

 

               The final variable uses the Intersects function to specifies that one feature intersects the geometry of the other                specified layer. FeatureSetByName creates a FeatureSet from a Feature Layer based on its name. That                feature set is identified through the $map, “Daily Raleigh Police Incidents” statement. $Feature is the                feature that is selected (the Police district in this example).

 

     b.  Add the loops

    

for(var f in intersectpts) {
    if (hasKey(listed, f.crime_category)==false) {
                    listed[f.crime_category]=1;
    } else {
        listed [f.crime_category] += 1
    }
}
for (var category in listed) {
crime = category + "  -  " + listed[category] + TextFormatting.NewLine;
    crimelist += crime
}
return crimelist

 

As previously mentioned, this section uses the for and HasKey functions to loop through the crime categories. It will add them to a dictionary and count how many instances there has been. I use the f.crime_category statement to identify the specific field that I need to aggregate and count. The second for loop constructs the final output which will be the crime category plus the count.

 

  1.       Add the expression to a Pop-Up Configuration. I like to use the Custom Attribute Display, I feel it provides a more informative, professional view. In the example below I am combining three Arcade expressions together to produce the final output. The first two were created by the City of Raleigh and the last {expression/expr4} is the example created above.

 

 

 

 

 

 

  1.       View your new pop up!

 

 

 

 

The entire expression:

var listed = {};
var counter = 0;
var crime = "";
var crimelist = "";
var intersectpts = Intersects($feature, FeatureSetByName($map,"Daily Raleigh Police Incidents"));
for(var f in intersectpts) {
    if (hasKey(listed, f.crime_category)==false) {
                    listed[f.crime_category]=1;
    } else {
        listed[f.crime_category] += 1
    }
}
for (var category in listed) {
    crime = category + "  -  " + listed [category] + TextFormatting.NewLine;
    crimelist += crime
}
return crimelist

 

 

But what if you have a number of stacked points? What if you would like to use one layer to reference attributes in another? A similar approach can be taken.

 

In this example I have a layer that contains (notional) human resources data for employees of a fictitious company. Each point represents a single employee, their office location and employment group. My second layer has a single point for each office location. Both layers share a common identifier that links an employee to an office. Ideally, I would like to see a single pop-up that displays details of the office and a break down on the number of employees per employment group. While I could implement this on the individual employee point layer it would mean that I need to have their stacked points visible on my map. Instead I will use the FeatureSet functionality with a filter to enable an office point to be selected and display the breakdown of employees per group. The example used in this blog is available to view in this Web Map.

 

 

The steps I took to produce this result are:

  1.       Create your Expression. The previous example can be used as a template.

          a. Add additional variables

var OfficeID = $feature["Office_ID"];
var HRDataID = FeatureSetByName($map,"FakeCo_HRStaff_Pt");
var FilterStatement = "Office_ID = @OfficeID";
var Dataset = filter(HRDataID, filterstatement);

 

 

               Because I need to reference features in a separate layer, I need to add some additional variables. The first                indicates the field that contains the unique key in the selected layer (the office location layer in this example).                The second uses the FeatureSetByName function to create a feature set for the employee location layer. The                FilterStatement variable links the unique key from our employee feature set to the previously defined OfficeID                variable. Finally, I use the Filter function, this will Filter a FeatureSet based on a SQL92 expression. In this                instance it will use my Filterstatement variable to filter based on the unique ID linking employees to their office.

 

           b. Modify the intersect to use a buffer and the newly filter data.

               

var intersectpts = Intersects(Dataset, Buffer($feature, 30, "meters"));

 

               In this step I modified the intersectpts variable to use my newly defined filter dataset (linking Offices to People).                As I am intersecting points, I am using a buffer of 30m to select the points that fall within that buffer distance.

 

           c. The full expression is:

 

var OfficeID = $feature["Office_ID"];
var HRDataID = FeatureSetByName($map,"FakeCo_HRStaff_Pt");
var FilterStatement = "Office_ID = @OfficeID";
var Dataset = filter(HRDataID, filterstatement);
var used = {};
var counter = 0;
var Group = "";
var Grouplist = "";
var intersectpts = Intersects(Dataset, Buffer($feature, 30, "meters"));
for(var f in intersectpts) {
    if (hasKey(used, f.Group_)==false) {
                    used[f.Group_]=1;
    } else {
        used[f.Group_] += 1
    }
}
for (var k in used) {
    Group = k + "  -  " + used[k] + TextFormatting.NewLine;
    Grouplist += Group
}
return Grouplist

 

  1.       Add the expression to a Pop-Up Configuration, again I suggest using the Custom Attribute Display.

 

 

     3.  Profit! (or view your layer).

 

 

What if you need to intersect points within the one layer? Easy, just remove the four filtering variables and change the intersect to use the FeatureSet function. I used the Staff Locations layer for the following example:

 

var used = {};
var counter = 0;
var Group = "";
var Grouplist = "";
var intersectpts = Intersects(FeatureSetByName($map,"FakeCo_HRStaff_Pt"), Buffer($feature, 30, "meters"));
for(var f in intersectpts) {
    if (hasKey(used, f.Group_)==false) {
                    used[f.Group_]=1;
    } else {
        used[f.Group_] += 1
    }
}
for (var k in used) {
    Group = k + "  -  " + used[k] + TextFormatting.NewLine;
    Grouplist += Group
}
return Grouplist

 

 

Hopefully this inspires you to use Arcade in new ways within ArcGIS Online!

 

 

Outcomes