Arcade - expression language

Overview

Arcade is a lightweight and secure expression language written for use in the ArcGIS platform. Like other expression languages, it can perform mathematical calculations and evaluate logical statements. It was designed specifically for creating custom visualizations and labeling expressions in the ArcGIS Platform, allowing users to write, share, and execute custom expressions in ArcGIS Pro, ArcGIS Online, the ArcGIS Runtime APIs, and the ArcGIS API for JavaScript.

What makes Arcade particularly unique to other expression and scripting languages is its inclusion of geometry functions. These geometry operations allow you to calculate areas and lengths, test topological relationships, and perform simple overlay operations. In the current release of the ArcGIS API for JavaScript, geometries can be created and referenced, but none of the geometry operations introduced in Arcade version 1.3 are supported in the 4.x series of the ArcGIS API for JavaScript.

Note: Geometry functions introduced in Arcade 1.3 are not supported in the 4.x series of the ArcGIS API for JavaScript. You may use them in versions 3.23 and greater of the 3.x series of the API. Support in 4.x is coming soon.

Syntax

In many respects Arcade's syntax is similar to JavaScript, allowing you to declare variables, perform logical operations, take advantage of built-in functions, and write custom functions. However, there are key differences between the two languages. Read the full Arcade documentation including guides and the function reference for more details on how to write an Arcade expression. You also have access to a playground that provides an environment for testing custom scripts based on your data.

Global variables may be used within Arcade expressions, giving them access to external data. Global variables contain external values that are passed to a script during its execution – based on the execution profile. The $feature global variable allows you to access field values for features in a FeatureLayer. Each field value is also considered a global variable. Field values are referenced using the following syntax:

$feature.fieldName

This makes it easy to perform simple calculations using field values at runtime.

// calculates the % of voters who voted for a
// democratic candidate
($feature.DEM_VOTES / $feature.TURNOUT ) * 100

Arcade is only executed within the context, or profile, in which it is understood. Within JavaScript apps, Arcade expressions are always referenced as a string value. You may use Arcade to write simple single-line expressions, or more complex multi-line expressions.

When writing single-line expressions, you can simply wrap it in double or single quotes.

renderer.valueExpression = "Round( ($feature.AGE_18UP / $feature.TOTAL_POP) * 100 )";

When writing multi-line expressions, we recommend you place it in a separate <script> tag outside the JavaScript portion of the app, and set the type to text/plain with a unique ID you can use to reference the script in the appropriate place within JavaScript.

<script type="text/plain" id="adult-population">
  // place multi-line Arcade expression here
</script>

Then you can reference the script as a string value by calling the document.getElementById() method.

renderer.valueExpression = document.getElementById("adult-population").text;

See the example snippets below and the Create a custom visualization using Arcade sample for more context.

Profiles

Arcade was designed for use in several profiles. A profile is a context in which the expression is understood and used. In the ArcGIS API 4.9 for JavaScript, Arcade supports three profiles: visualization and popups and labeling (currently labeling is only supported in 3D). In 3D SceneViews, calculating the elevation of a feature can also be done using Arcade.

Visualization

In the visualization profile, Arcade allows you to calculate values for each feature in a FeatureLayer or SceneLayer at runtime and use those values as the basis for a data-driven visualization. This is an alternative approach to creating data-driven visualizations based on a single field value in the layer. To accomplish this, an Arcade expression may be passed to the valueExpression property in ClassBreaksRenderer, UniqueValueRenderer or any of the visual variables: color, size, opacity, and rotation instead of referencing a field/normalizationField or a JavaScript function.

Arcade supports creating visualizations for FeatureLayer and SceneLayer only.

Other layer types that accept ClassBreaksRenderer and UniqueValueRenderer, such as MapImageLayer, do not support Arcade.

When used in a ClassBreaksRenderer or any of the visual variables, the expression must evaluate to a number. Expressions may evaluate to either strings or numbers in UniqueValueRenderer.

In the example below, an Arcade expression is used in the valueExpression property of a UniqueValueRenderer. In this case, we are creating a visualization for a FeatureLayer representing U.S. counties. The service has three fields that identify the number of republicans, democrats, and independent/non-party voters in each county. We would like to visualize each county based on which party outnumbers the others. Since the service does not contain a field indicating the predominant party, we can write an Arcade expression to identify that for each feature.

First, write the Arcade expression in a script tag with a unique ID.

<script type="text/plain" id="winning-party">
  // Write the expression and reference the value
  // of each field with a meaningful variable name within
  // the expression. Then calculate the max number with
  // the Max() function and use Decode() to return a string
  // value representing the party whose field value matches
  // the max value.

  var republican = $feature.MP06025a_B;
  var democrat = $feature.MP06024a_B;
  var independent = $feature.MP06026a_B;
  var parties = [republican, democrat, independent];
  // Decode() and Max() are built-in Arcade functions
  return Decode( Max(parties),
    republican, 'republican',
    democrat, 'democrat',
    independent, 'independent',
    'n/a');
</script>

Then reference the script as a string value using document.getElementById() within the appropriate valueExpression property in the JavaScript.

// Assign the expression to the `valueExpression` property and
// set up the unique value infos based on the decode values
// you set up in the expression.

var winnerArcade = document.getElementById("winning-party").text;

var renderer = new UniqueValueRenderer({
  valueExpression: winnerArcade,
  valueExpressionTitle: "Counties by dominant party among registered voters",
  uniqueValueInfos: [{
    value: "democrat",
    symbol: createSymbol("#00c3ff"),
    label: "Democrat"
  }, {
    value: "republican",
    symbol: createSymbol("#ff002e"),
    label: "Republican"
  }, {
    value: "independent",
    symbol: createSymbol("#faff00"),
    label: "Independent/non-affiliated"
  }]
});

You can also add an opacity visual variable to the renderer to visualize the relative strength of the predominant party in each county. Counties where more people come from a single party will be drawn with high opacity, while those where the proportion of people from each party are relatively equal will be drawn with low opacity.

First, write the expression in a <script> tag.

<script type="text/plain" id="strength">
  // Write the expression and reference the value
  // of each field with a meaningful variable name within
  // the expression. Then calculate the max number with
  // the Max() function and the total using Sum().
  // Calculate the share of the max population within the
  // county. This value will be between 33 - 100 and will
  // be used to determine the feature's opacity.
  // Note the value is explicitly returned; it could also
  // be implicitly returned like the previous example

  var republican = $feature.MP06025a_B;
  var democrat = $feature.MP06024a_B;
  var independent = $feature.MP06026a_B;
  var parties = [republican, democrat, independent];
  var total = Sum(parties);
  var max = Max(parties);

  return (max / total) * 100;
</script>

Then reference it in JavaScript as a string value.

// Assign the expression to the `valueExpression` property and
// set up the unique value infos based on the decode values
// you set up in the expression.

var strengthArcade = document.getElementById("strength").text;

var opacityVV = {
  type: "opacity",
  valueExpression: strengthArcade,
  stops: [
    { value: 33, opacity: 0.1 },
    { value: 50, opacity: 1.0 }
  ]
};

// Add the visual variable to the renderer

renderer.visualVariables = [ opacityVV ];

View the Create a custom visualization using Arcade sample to see this example in context.

Popups

Arcade expressions can also be referenced within the content of a PopupTemplate. Similar to the visualization profile, this is useful for situations when you want to display data that isn't present as an attribute value in your FeatureLayer instance.

For example, the Reference Arcade expressions in PopupTemplate sample displays a layer containing labor statistics for each U.S. county. Some attributes include unemployment rate, population, and the number of people participating in the labor force. It does not have an attribute for labor force participation rate. We can use Arcade to calculate that for us at runtime.

// labor force participation rate
Round(($feature.CIVLBFR_CY / $feature.POP_16UP)*100,2)

The values returned from this expression can be used to visualize the layer and/or displayed in the layer's popupTemplate. To view the value in the popup, we must reference it in the expressionInfos property of the PopupTemplate and assign it a name and a title.

layer.popupTemplate = {
  expressionInfos: [{
    name: "participation-rate",
    title: "% of population 16+ participating in the labor force",
    expression: "Round(($feature.CIVLBFR_CY / $feature.POP_16UP)*100,2)"
  }],
  content: "In {NAME} county, {expression/participation-rate}% of the population"
    + " participates in the labor force."
};

Notice that once the expression exists in the expressionInfos property, you can reference the value returned from the expression using the {expression/expression-name} placeholder template within the content of the PopupTemplate. The Popup's content would display the following after the user clicked a feature representing Greenlee, AZ:

arcade-popup-text

You can also reference values returned from Arcade expressions inside the fieldInfos property of the PopupTemplate's content so they can be displayed in tabular format. Just reference the name of the expression in the fieldName property of the object. Remember to use the expression/expression-name syntax.

layer.popupTemplate = {
  expressionInfos: [{
    name: "participation-rate",
    title: "% of population 16+ participating in the labor force",
    expression: "Round(($feature.CIVLBFR_CY / $feature.POP_16UP)*100,2)"
  }],
  content: [{
    type: "fields",
    fieldInfos: [{
      fieldName: "expression/participation-rate"
    }]
  }]
};

The popup will display the following:

arcade-popup-table

Note that you can also take advantage of the formatting options of the PopupTemplate's fieldInfos property to format numbers returned from expressions.

View the Reference Arcade expressions in PopupTemplate sample to see this workflow in the context of a live sample application.

Labeling

Arcade may also be used to create label expressions for features in a FeatureLayer or SceneLayer. Starting at version 4.5 of the API, this is the only supported method for labeling features.

You must add at least one LabelClass to the labelingInfo property of the layer. The Arcade expression must be passed as a string value to the expression property of the LabelClass's labelExpressionInfo object.

// returns the value of a field in the layer
// the value of this field will be the label for each feature
var arcade = "$feature.STATION_NAME";

// this object autocasts as new LabelClass()
var labelClass = {
  // set the arcade expression to the `expression` property of labelExpressionInfo
  labelExpressionInfo: {
    expression: arcade
  },
  labelPlacement: "below-right",
  minScale: 2500000,
  symbol: {
    type: "label-3d",
    symbolLayers: [{
      type: "text",
      material: { color: "white" },
      halo: {
        color: "black",
        size: 1
      },
      size: 8
    }]
  }
};

// set the label class to the feature layer
featureLayer.labelingInfo = [ labelClass ];

Label expressions written in Arcade may be more complex, containing multiple lines that perform mathematical and logical operations. For example, the Multi-line labels demonstrates a how to construct a more complex multi-line label expression. This expression assigns two numeric field values to their own variables, evaluates them, and returns a string value. Arcade's When() function is used to evaluate the wind direction (between 0-360 degrees), which returns the associated compass direction of either N, NE, E, SE, S, SW, W, or NW. If the wind speed is 0, then no direction is returned. The final line of the expression returns the label, which is the value of the WIND variable.

<script type="text/plain" id="wind-direction">
  var DEG = $feature.WIND_DIRECT;
  var SPEED = $feature.WIND_SPEED;
  var DIR = When( SPEED == 0, '',
    (DEG < 22.5 && DEG >= 0) || DEG > 337.5, 'N',
     DEG >= 22.5 && DEG < 67.5, 'NE',
     DEG >= 67.5 && DEG < 112.5, 'E',
     DEG >= 112.5 && DEG < 157.5, 'SE',
     DEG >= 157.5 && DEG < 202.5, 'S',
     DEG >= 202.5 && DEG < 247.5, 'SW',
     DEG >= 247.5 && DEG < 292.5, 'W',
     DEG >= 292.5 && DEG < 337.5, 'NW', '' );
  var WIND = SPEED + ' mph ' + DIR;
  return WIND;
</script>

There are many additional Arcade functions useful for labeling, including text functions that provide logic for text formatting. Be sure to check out the full Arcade documentation for more information regarding these built-in functions.

Other cases

Elevation

In 3D SceneViews you can set custom elevations on a feature-by-feature basis using Arcade expressions. Although features can have z values, you might want to calculate their height based on an attribute value, or derive it from a formula using the z value. You may also have data that doesn't contain z-values, but has z-related information stored in an attribute field. In these cases you can set an expression in the featureExpressionInfo.expression property. As an example, the Elevation options sample shows how to change the height of point symbols using Arcade:

layer.elevationInfo = {
  mode: "absolute-height",
  featureExpressionInfo: {
    expression: "Geometry($feature).z + $feature.HEIGHT"
  },
  unit: "meters"
};

In the example above a custom attribute HEIGHT is added to the z value of the geometry to set the final graphic elevation. For line or polygon features, all the vertices of the feature will have the elevation returned by expression.

You can use Arcade playground to build, debug, and test an Arcade expression within a browser based on field values imported from an input feature service. It also includes the ability to debug complex scripts with a Console() function.