Streamline field data collection in QuickCapture using Arcade

1226
1
12-01-2023 07:32 AM
BrandyPerkins
Esri Contributor
3 1 1,226

At version 1.18 of the QuickCapture mobile app, you can use Arcade expressions to calculate field values in captured records. Arcade provides another option to populate QuickCapture records beyond fixed values, user inputs, and device variables. You can calculate values with expressions using Arcade functions, operators, and input values from other fields in the target layer, or from other layers in the project web map.

Why use Arcade

Use ArcGIS Arcade expressions to streamline populating attributes for data field collection in ArcGIS QuickCapture and take advantage of Arcade's ability to reference and query other attributes and layers in the map.

Arcade is a scripting language used in ArcGIS to perform calculations, manipulate data, and create custom visualizations. Using the integrated Arcade Editor in QuickCapture allows you to author expressions to calculate a value that gets written to a specific field.

Arcade provides access to attribute values from other fields in the target layer or values and feature geometry from other layers in the project’s web map, allowing you to create expressions that include spatial operations such as buffer and intersect. For example, you can populate a field based on a point in polygon query that gets the name of a region in which a point feature was captured. Each expression you add is added to a list of expressions that you can reuse throughout the project.

Writing expressions in the integrated Arcade editor

The Arcade editor is where you'll create and edit Arcade expressions. In the QuickCapture web designer, you just need to select a project button, go to the Data tab, and for the field that you want to update, select Arcade expression as the input.

Arcade expression selected as input for a Duration fieldArcade expression selected as input for a Duration field

Selecting Create new will launch the expression builder where you can write your first expression. Here is an overview of its functionality:

  • The label input allows you to enter a unique name for your expression. This is helpful if you want to re-use it across multiple buttons and fields.
  • The expression window is where you write the code. You can copy and paste samples like the examples provided here and then modify values to match your data. Or you can write   expressions using the options in the builder.
  • The Profile variables tab offers a list of predefined variables, including attribute fields and geometry for the layer features and the project map.
  • The Functions tab lists the Arcade functions. You can click the arrow next to each function to access its integrated help on parameters, syntax, and usage.

Let's look at some common use cases.

Calculate a value by referencing other fields

To save time and minimize user interactions, it can be useful to write expressions that reference other fields in the captured record. You can calculate a value based on one or more fields that may have been populated from fixed values, user inputs, or device variables.

Example 1 – I want to record the horizontal position accuracy as a category instead of a number

QuickCapture allows you to record the position accuracy of features you capture using the horizontal accuracy (m) QuickCapture  variable. This returns a value in meters, for example, 10.

However, if you wanted to record the accuracy as good, average, or poor, you could use the following logic:

  • If horizontal accuracy < 5, return ‘good’
  • If horizontal accuracy >= 5 AND horizontal accuracy < 10, return ‘average’
  • Otherwise, return ‘poor’

 

When($feature.horizontalAccuracy < 5, 'good', $feature.horizontalAccuracy >=5 && $feature.horizontalAccuracy < 10, 'average', 'poor')

 

*To use this expression with your own data, replace horizontalAccuracy with the field name from your target layer (such as esrignss_h_rms).

💡TIP: When using $feature, you can see the field options as suggestions in the editor when you type the dot.  Or you can insert the full phrase from the Profile variables tab by selecting from the list of values—including one that gets the feature's geometry to use for spatial queries. In the Arcade window, click the Profile variables tab, and click the arrow next to $feature. In the list of fields, select the field to complete the statement in the editor.

Example 2 - I want to calculate the duration for a captured track

For line and polygon buttons, QuickCapture supports the ${startTime} and ${endTime} device variables. These variables capture the time that a line or polygon capture was started and stopped. We also have variables to calculate the length of the captured line, but none that record its time-based duration.

In Arcade, there is a function called DateDiff(), which takes two date times and calculates the difference between them. If you configure your project to record the start time and end time in separate fields, you could use the following expression:

 

//Get the difference between the end and start times in seconds
//and convert to minutes
Round(DateDiff($feature.end_time, $feature.start_time, "seconds"), 1) / 60;

 

This expression calculates the time difference between two attributes and displays it in minutes with one decimal place precision. QuickCapture can then write this duration to the specified field in the captured feature.

  • The DateDiff() function calculates the time difference in seconds between the end_time and start_time attributes of the feature.
  • The Round function rounds the result of the time difference to one decimal place.
  • The rounded result is divided by 60 to convert the value from seconds to minutes.

NOTE: Although the DateDiff() function supports “minutes” as the unit parameter, using “minutes” returns the result as an integer (due to a Maps SDK bug). Therefore, the workaround is to return the result in seconds, and then manually convert it to minutes.

*To use this expression with your own data, swap end_time and start_time with the field names from your target layer.

Example 3 - I want to get layer statistics

Suppose you wanted to calculate the difference between a captured feature's population and the average population of the other features in the target layer. The Arcade expression would look like this:

 

//Get the ‘population’ value from captured record
//and subtract it from the average ‘population’ for the target feature layer
$feature.population - Average($layer, 'population');

 

This expression gets the layer average for the population field and subtracts it from the population value entered in the captured feature.

Note that $layer is the feature layer that QuickCapture is sending features to. So we can do things like get a feature count or summary statistic for any of the fields in this layer. You should note that the value returned by the calculation will exclude the feature that you are about to send from QuickCapture because the expression runs before sending the record.

*To use this expression with your own data, swap population with the field name from your layer.

💡TIP: To get a field name from the target layer, click the $layer profile variable and select a value in the Field names section.

Point in polygon queries

You can also build an Arcade expression that queries other layers from your map and returns information from those layers to populate a field attribute for the new feature. In the web map configured for your project, add a feature layer from which you want the data. Use Arcade to access that layer as a FeatureSet (set of point, line, or polygon features in the map) and specify its information to use for attribute and spatial queries, such as point in polygon.

💡TIP: Search ArcGIS Living Atlas of the World for hosted feature layers to add to your web map and access with Arcade expressions. You can find layers about many different topics such as demographics and infrastructure.

Although you need to be online for Arcade expressions to run in QuickCapture, you can still capture your features while offline. The Arcade expressions will automatically run once the device is back online (and the map loaded) and will be uploaded to ArcGIS.

NOTE: While all layers referenced by your Arcade expression need to be online feature layers or tables (not Mobile Map Packages), Arcade expressions in QuickCapture will work whether you capture data online or offline.

The following examples use the Intersects function to find a feature in the map ($map) that intersects the feature captured by the button ($feature).

Example 4 - I want to know the name of the region a point was captured in

It can be valuable to store the name of the region where a feature was collected. You could have the user manually select the region from a choice list (if they know it), or you could post-process the data. However, both these options require additional effort. A more efficient way would be to use an Arcade expression to get the name by performing a spatial intersection between the captured feature's location and a polygon layer that represents the regions.

The following Arcade expression searches the web map for a layer called Regions and performs an intersect using the location of the captured feature. It then returns the name value from the selected polygon feature.

NOTE: If your expression references a polygon layer by name, you must include that layer in the web map that’s configured for your QuickCapture project. Ensure that the layer name and field name in your expression matches what’s used in the web map layer.

 

// Create a feature set using the 'Regions' layer in the map
var regions = FeatureSetByName($map, "Regions", ["REGION"]);

// Intersect the current location with the regions and
// get the first region
var region = First(Intersects($feature, regions));

// If the current location does intersect a feature,
// return the name of the region. Otherwise, return null
if (!IsEmpty(region)) {
  return region["REGION"];
} else {
  return null;
}

 

*To use this expression with your own data, swap the “Regions” layer name with the layer name from the map in your project and swap “REGION” with the field name from the layer that you want to return.

This example uses geographic regions, however, it would work for any polygon layer, such as land parcels, vegetation types, population count, and so on.

Example 5: I want to know the census block name for the start and end of a captured line

Another variation using the Intersect function is to perform two intersects between a boundary layer and the start and end of a line feature captured in QuickCapture. For example, if your project is configured to record a track that represents the path a user traveled, you can use Arcade to also record the NAME of the Census blocks in which they started and ended.

You’d need to write two separate expressions to update the start_block and end_block fields in the track layer based on which feature each intersects in a Census block layer. The 'Geometry' function retrieves the geometry of the current feature and accesses the first/last point of the first path in the feature set. The 'Intersects' function checks for the intersection between the 'Start_point'/'End_point' and the feature in the 'Blocks' feature set. If an intersecting block is found, it returns the value of the 'NAME' attribute from the 'Block' feature.

Start block

 

// Create a feature set using the 'U.S. Census Blocks' layer in the map
var Blocks = FeatureSetByName($map, "U.S. Census Blocks", ["NAME"]);

// Get the point geometry of the start of the line
var Start_point = Geometry($feature).paths[0][0];

// Intersect the current location with the Census Blocks and
// get the first Block
var Block = First(Intersects(Start_point, Blocks));

// If the current location does intersect a feature,
// return the name of the Block. Otherwise, return null
if (!IsEmpty(Block)) {
  return Block["NAME"];
} else {
  return null;
}

 

End block

 

// Create a feature set using the 'U.S. Census Blocks' layer in the map
var Blocks = FeatureSetByName($map, "U.S. Census Blocks", ["NAME"]);

// Get the point geometry of the end of the line
var End_point = Geometry($feature).paths[-1][-1];

// Intersect the current location with the Census Blocks and
// get the first Block
var Block = First(Intersects(End_point, Blocks));

// If the current location does intersect a feature,
// return the name of the Block. Otherwise, return null
if (!IsEmpty(Block)) {
  return Block["NAME"];
} else {
  return null;
}

 

*To use these expressions with your own data, swap the “U.S. Census Blocks” layer name with the layer name from the map in your project and swap “NAME” with the field name from the layer.

Get attributes from a nearby feature

When you need to determine a specific location in the map that's nearest to the captured feature, you can use Arcade by finding the closest feature within a specified distance. For example, you could pair the Intersects and Buffer functions to populate a distance or name field, such as for the nearest medical facility, train station, or tree.

Example 6: I want to know the name of the feature nearest to the captured point

This example gets the name of the nearest medical facility. Here’s a summary of what the expression does:

  1. Declare all the variables.
  2. Get the geometry of the captured feature.
  3. Cycle through the features that intersect the 2000-meter buffer and get the minimum distance.
  4. The distance in meters is calculated between the current location and each point in the buffered area.
  5. If the distance between the feature and the last point is less, save the distance and name of the closest point. If the distance value is larger, ignore it.
  6. After iterating through all the features that intersect the buffer, return the name of the closest feature: the one with the smallest distance returned.

 

var facilities = FeatureSetByName($map, "Medical");
var point_fs = Intersects(facilities, Buffer($feature, 2000, "meters"));
var min_dist = 99999;
var nearest_medicalName = null;
var geo = Geometry($feature);
for (var point in point_fs) {
  var point_geo = Geometry(point);
  var dist = Distance(geo, point_geo, "meters");
  if (dist < min_dist) {
    min_dist = dist;
    nearest_medicalName = point.name;
  }
}
return nearest_medicalName;

 

*To use this expression with your own data, swap the “Medical” layer name with the layer name from the map used by your project and swap name (in point.name) with the field name from the layer. Adjust the buffer distance and preferred units of measurement.

Troubleshooting and known limits

Next, we'll cover some things that you need to be aware of when writing your own expressions, especially if you're planning to write them from scratch.

Arcade version

  • The QuickCapture mobile app uses Arcade version 1.18. When using the Arcade Editor in the web designer (which uses Arcade version 1.24), refer to the integrated Arcade help to verify that the function you’re inserting is supported in version 1.18 or earlier. For example, if you search for DateOnly() on the Functions tab, the in-app help indicates that it’s available “since version 1.24,” so it’s not supported in the QuickCapture mobile app. Refer to the Arcade version matrix and release notes for details.

Supported field types

  • The mobile app cannot run calculations if the expression references system managed fields OBJECTID, GlobalID, CreationDate, Creator, EditDate, and Editor) because the app does not know these field values until the feature is submitted. To get a value for a user name or capture date, the expression must reference a field that’s populated with a device variable.
  • Currently, project layers that contain the ArcGIS field types that were introduced in 2023 (DateOnly, TimeOnly, TimestampOffset, BigInteger) cannot use Arcade expressions in QuickCapture (they’re not supported by the Arcade version used by the mobile app).  As a workaround, if you have a layer with these field types, create a hosted feature layer view that excludes them and use the layer view in the project instead.

    💡TIP: Arcade allows you to cast a value of one data type to another type. For example, to better format dates. It is best practice to handle casting within the script for full control of casting behavior to Number, Date, or Text return types.

Spatial reference

  • Arcade will use the spatial reference from the web map defined in your project. It will assume that all layers in the map share the same spatial reference when using geometry functions. Therefore, the mixing of spatial references is not supported.

Reasons your expression may not run

  • The expression is invalid. For example, a typo, an invalid field name, or the QuickCapture project is not referencing the correct web map.
  • If your expression uses a field from a feature, you'll get a warning when testing in the Arcade Editor window unless you collect at least one feature: The editor runs the expression on the first feature in the layer, so if you have an empty feature layer for a new project, the test won't run successfully.

Resources

  • See Prepare a feature layer in the QuickCapture documentation for specific requirements. You must add fields to the feature layer for storing the information to be collected by the project.
  • Refer to the ArcGIS QuickCapture documentation about using Arcade in your projects.
  • If you’re not familiar with Arcade, you can find several Arcade resources in the Arcade documentation, including details about the QuickCapture profile.
  • Try out the Arcade editor and test expressions in the Arcade playground.
  • If you’re new to Arcade, learn more and practice using it with the Try Arcade tutorial series, which includes step-by-step workflows that use sample data.
  •  For instructions specific to using Arcade in the QuickCapture web designer, see this tutorial.
1 Comment