Calculated expressions in ArcGIS Field Maps streamline all kinds of data collection workflows from storing location as an attribute, to pulling attributes from related records or other layers in the map. In this blog post, we’ll walk through some of the most common use cases and provide sample code to get you up and running with calculated expressions in your own forms.
If you’re not familiar with Arcade, you can read the documentation or try using it in the playground. For step-by-step instructions on how to add expressions to the form in Field Maps Designer, see Add calculated expressions. Let’s jump right in and see what’s possible!
1. Fetch an attribute from an underlying polygon
Sometimes, it can be valuable to store the geographic area a feature was collected in. In the past, this could be done manually by selecting the region from a list of values or by post processing the data. Now, this can be streamlined and automatically captured in the field by using a form calculation that performs a spatial intersection of the current location and a layer of polygons representing the geographic areas.
Example: I am recording bird sightings and want to automatically store the region I’m in.
// Create a feature set using the 'Regions' layer in the map
var regions = FeatureSetByName($map, 'Regions', ['name'])
// 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['name']
} else {
return null
}
2. Fetch an attribute from a related record
When performing inspections, it’s often necessary to store information about the parent feature such as the address, or type of asset. These attributes can be pulled from the parent layer by querying the related records and accessing the necessary attributes.
Example: I’m inspecting hydrants and need to record the Facility ID with each inspection.
// Get the feature set for the hydrants
var hydrants = FeatureSetByRelationshipName($feature, 'wHydrant', ['facilityid'], true)
// Get the first hydrant (should only be one)
var hydrant = First(hydrants)
// If there was a hydrant, return the facilityid of it,
// Otherwise, return null
if (!IsEmpty(hydrant)) {
return hydrant['facilityid']
} else {
return null
}
3. Store a user’s name, email address, or username
When performing inspections or collecting new data, editor tracking works great for storing the username of the person who created or edited the feature, but it doesn’t store the user’s full name or email address. Having these extra attributes can make it much easier to understand the data when displayed in reports, maps, and dashboards. This can be accomplished by getting the signed in user’s info via Arcade.
Example: I’m filling out a damage inspection report and need to provide my name.
GetUser($layer).fullName
Note: At version 22.3 of Field Maps, this does not work if the device is offline.
4. Calculate the current date and time
When performing inspections, it’s often useful to set the inspection date to the current date and time. While the mobile worker can do this in the mobile app with a few taps, it can be more efficient to auto-populate this information using an Arcade expression.
Example: I’m filling out a damage inspection report and need to set the inspection date.
Now()
5. Store the geometry as an attribute
When integrating with other systems, it can be useful to store aspects of the geometry as separate attributes. This can be done by accessing the feature’s geometry via Arcade.
Example: When working with high accuracy GNSS receivers, the orthometric height is stored in the z-value of the geometry and I need to also store and display the orthometric height as an attribute value for point features collected in the field.
// Get the Z value
var geom = Geometry($feature)
if (IsEmpty(geom)) {
return null
} else {
return geom.Z
}
Sometimes, the raw x and y location are not what you need. You might need the latitude and longitude. If your map and data are using Web Mercator Projection you can write code to calculate the latitude and longitude from the x, y meter values.
Example: I need to store the latitude and longitude values to conform to some standard data collection specification.
// Create a function to convert meters to lat, long
// Source: http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/
function MetersToLatLon(geometry) {
if (IsEmpty(geometry)) {
return [null, null]
}
var originShift = 2.0 * PI * 6378137.0 / 2.0
var lon = (geometry.x / originShift) * 180.0
var lat = (geometry.y / originShift) * 180.0
lat = 180.0 / PI * (2.0 * Atan( Exp( lat * PI / 180.0)) - PI / 2.0)
return [Round(lat, 6), Round(lon, 6)]
}
// Call the function and return the latitude or longitude value
MetersToLatLon(Geometry($feature))[0]
When collecting or updating the geometry of a feature, you may want to automatically calculate the length of a line or the area of a polygon as a field value. Use the AreaGeodetic() function or LengthGeodetic() function. to calculate length or area using the feature geometry instead of using the Shape_Area and Shape_Length fields. Those fields are updated after edits are submitted and your calculations may not reflect changes to the geometry.
Example: I need to store the acreage of a forest stand as an attribute and would like to round the value to 3 decimal places.
// Calculate the area using the geometry
var geom = Geometry($feature)
if (IsEmpty(geom)) {
return null
} else {
return Round(AreaGeodetic($feature, 'acres'), 3)
}
6. Calculate a value from other fields
Often, it can be useful to calculate a value based on one or more fields that the mobile user fills out.
Example: I need to calculate the score when doing a damage assessment report. Certain things (e.g., the roof, foundation, habitability.) are scored based on their damage level. I need to sum all these independent scores into a single value to use for filtering and visualization.
$feature["foundation_condition"] + $feature["roof_condition"] + $feature["habitability"]
7. Store info about a nearby feature
When installing new assets or planting trees, it’s often useful to store the nearest address.
Example: I’m planting new trees in a neighborhood and want to store the nearest address using existing parcel data.
// If feature doesn't have geometry return null
if (IsEmpty(Geometry($feature))) { return null }
// Get the parcels layer
var parcels = FeatureSetByName($map, 'Parcels')
// Buffer the current location and intersect with parcels
var bufferedLocation = Buffer($feature, 100, 'feet')
var candidateParcels = Intersects(parcels, bufferedLocation)
// Calculate the distance between the parcel and the current location
// Store the feature and distance as a dictionary and push it into an array
var featuresWithDistances = []
for (var f in candidateParcels) {
Push(featuresWithDistances,
{
'distance': Distance($feature, f, 'feet'),
'feature': f
}
)
}
// Sort the candidate parcels by distance using a custom function
function sortByDistance(a, b) {
return a['distance'] - b['distance']
}
var sorted = Sort(featuresWithDistances, sortByDistance)
// Get the closest feature
var closestFeatureWithDistance = First(sorted)
// If there was no feature, return null
if (IsEmpty(closestFeatureWithDistance)) { return null }
// Return the address
return `${closestFeatureWithDistance['feature']['ADDNUM']} ${closestFeatureWithDistance['feature']['ADDRESSNAM']}`
Summary
We’ve shown quite a few different examples here, there’s so much more that you can do with Arcade and form calculations though! If you have any questions or would like to share how you use form calculations please post in the ArcGIS Field Maps Community forum or email us at arcgisfieldmaps@esri.com.
Article Discussion: