ArcGIS Blog

Mapping

ArcGIS Maps SDK for JavaScript

Using Arcade expressions in web apps

By Kristian Ekenes

At the end of 2016 Esri released Arcade, a light-weight scripting language designed to evaluate expressions in labeling and rendering contexts with apps built on the ArcGIS platform. Arcade is similar to other scripting and expression languages, such as SQL, Python, VBScript and spreadsheet formulas. These languages have been supported and used in ArcGIS to perform various tasks, but they aren’t portable and your scripts cannot be shared everywhere. Arcade will provide that portability going forward. It has syntax similar to JavaScript, including its own library of functions that allow you to perform calculations and format text.

This post is intended to be an introductory guide demonstrating how and why you may want to use Arcade expressions in custom web apps built with the ArcGIS API for JavaScript.

Basic syntax

As stated in the Getting Started page in the Arcade guide, Arcade expressions may either be single-line or multi-line expressions. An expression has access to field values for the layer in which the expression is used. Each field value is a global variable that may be accessed with the following syntax:

$feature.FIELD_NAME

This makes it handy to manipulate field values for readability, conversion, or reclassification purposes.

// converts values from miles per hour (mph) 
// to kilometers per hour (kph)
$feature.SPEED_LIMIT * 1.60934

// Normalizes values and converts them to percentages
Round(($feature.POP_GRADUATED / $feature.ADULT_POP ) * 100, 2)

// If the field is already normalized...
Round($feature.COMPLETED * 100, 2)

// Or simple if/else statements for
// classifying data into categories
IIF($feature.COUNT >= 1000, "quota met", "needs improvement")

Be sure to read more about Arcade’s syntax and the full function documentation to learn how to use field values for visualization and labeling purposes.

Visualizations driven by Arcade expressions

I’ll use the following sample to demonstrate how to use Arcade in a data-driven visualization within a custom app built with version 4.2 of the JavaScript API. This sample visualizes the apparent temperature (the estimated temperature people actually feel depending on factors such as wind chill and humidity) measured at weather stations worldwide. In the sample, I use Arcade to calculate wind chill and the heat index based on wind speed, air temperature, and relative humidity numbers provided as attributes in the service. The expression then determines which variable (heat index or wind chill) to use as the apparent temperature.

Once written, the expression is set on the valueExpression property of the renderer or visual variable used for the visualization.

Using Arcade eliminates the need to add new fields to a service layer and perform a series of field calculations to return these same results.

Arcade expressions are always referenced in JavaScript apps as string values. For simple one-line expressions like those listed in the Basic syntax section of this post, you can just wrap the expression in double or single quotes.

For multi-line Arcade expressions used in web apps, you can place the expression inside a script HTML tag. Be sure to set the MIME type to text/plain so the browser knows to not attempt to execute the script as JavaScript. The snippet below contains the expression that determines apparent temperature.

<script type="text/plain" id="apparent-temp">
  // if no temp, rh, or ws is available don't render feature
  if(  IsEmpty($feature.TEMP) 
    || IsEmpty($feature.R_HUMIDITY)
    || IsEmpty($feature.WIND_SPEED)
  ){
    return null;
  }

  var t = $feature.TEMP;
  var ws = $feature.WIND_SPEED * 0.621371;  // convert to mph
  var r = $feature.R_HUMIDITY;

  //// WIND CHILL ////

  var wc = 35.74 + (0.6215 * t) 
    - (35.75 * Pow(ws, 0.16)) 
    + (0.4275 * t * Pow(ws, 0.16));

  //// HEAT INDEX ////

  var c1 = -42.379;
  var c2 = 2.04901523;
  var c3 = 10.14333127;
  var c4 = -0.22475541;
  var c5 = -0.00683783;
  var c6 = -0.05481717;
  var c7 = 0.00122874;
  var c8 = 0.00085282;
  var c9 = -0.00000199;

  var hi = c1 + (c2 * t) + (c3 * r) 
    + (c4 * t * r) + (c5 * t * t) 
    + (c6 * r * r) + (c7 * t * t * r) 
    + (c8 * t * r * r) + (c9 * t * t * r * r);

  // apparent temperature
  var apTemp = WHEN(  
      // Only for temps below 50F and WS > 3mph
      // wind chill must be lower than the temperature
    t <= 50 && ws >= 3 && wc < t, wc,
      // Only for temps above 80F and RH above 40%
    t >= 80 && r >= 40, hi, 
      // all other points will display air temperature
    t
  );
  return apTemp;
</script>

Since browsers won’t recognize the contents of a text/plain script as JavaScript, they will not attempt to execute it outside of its intended environment. Instead, we give the script a unique ID to identify it for later use in JavaScript.

Now that the script is written, we can reference it as a string value using the document.getElementById() method to get the content of the tag as text.

var apparentTempArcade = document.getElementById("apparent-temp").text;

Now the expression is a string value referenced by the apparentTempArcade variable. All I have to do is reference this variable in the appropriate valueExpression property in the desired renderer or visual variable.

var apparentTempRenderer = new SimpleRenderer({
  symbol: createSymbol(),
  label: "Weather station",
  visualVariables: [{
    type: "color",
    valueExpression: apparentTempArcade,
    valueExpressionTitle: "Apparent temperature (°F)",
    stops: [
      { value: -15, color: "#00C5FF" },
      { value: 12, color: "#BEE8FF" },
      { value: 32, color: "#fdf4d2" },
      { value: 64, color: "#FF7F7F" },
      { value: 82, color: "#E60000" }
    ]
  }]
});

This expression will execute for each feature in the layer within the context of the renderer and return the apparent temperature for each given point. In the renderer’s color visual variable, we map specific temperatures to colors for visualization purposes.

Take a look at the rest of the code in the sample. I wrote five Arcade expressions, each used in a different renderer within the app.

Benefits of Arcade

After seeing these examples, you may ask, “Can’t I already do all of this by passing JavaScript functions to renderers and visual variables?” The answer is yes, you can. But Arcade provides a number of advantages other expression languages, including JavaScript, do not.

For example, Arcade is secure – it cannot be injected with executable code outside of its intended context. It is also portable and sharable; expressions can be authored in an app using one ArcGIS API, and understood by another without syntax differences. That means you can author an expression for labeling features in ArcGIS Online and that same expression will render labels the same way in ArcGIS Pro or any app written with the ArcGIS API for JavaScript or any of the Runtime APIs consuming the same layer. And Arcade is extendable. The Arcade team at Esri can add functions specific to GIS applications, such as geometry calculations, to Arcade’s function library as use cases and demand drive the need for them.

Sharing expressions

Driving visualizations with client-side calculations has been supported in the ArcGIS API for JavaScript via custom JavaScript functions for some time now. However, these visualizations could only be used in custom web apps and not saved to web maps nor shared to other applications. Arcade Expressions now bridge that gap and allow you to base visualizations off values returned from custom calculations, save them in web map items, and share those custom visualizations to be consumed by other web, desktop, and mobile applications.

To demonstrate how visualizations driven by Arcade may be shared between applications, open this web map which displays the same weather stations layer. This layer uses Arcade to visualize the difference between my wind chill calculation and the wind chill value stored in the service. Now open this app, which demonstrates how to load the web map into a custom JavaScript application. This example is based off the Web map by ID sample in the 3.19 documentation. You will see the same layer visualized the same way as it is in the ArcGIS Online map viewer. Save the layer to a new web map in your organization and try loading it into ArcGIS Pro. Even though the Arcade expression was written and stored in the ArcGIS Online web map, it can now be loaded and understood in custom web apps and ArcGIS Pro.

Rather than use a web map, you can alternately save the visualization directly to the layer within the map viewer, or by using the Visualization tab in ArcGIS Online. In the example below, I created a bivariate visualization using two Arcade expressions. Color is driven by the apparent temperature expression and size is driven by the difference between apparent temperature and the measured air temperature.

Then you can load the layer with the saved visualization using the item id of the layer. This is the same id used in the URL of the item.

View the app    View the code

Layer.fromPortalItem({
  portalItem: { // autocast as esri/portal/PortalItem
    id: "05b6108388d14204a61cf1fc410bf5f4"
  }
}).then(addLayer);

// Adds the layer to the map once it loads
function addLayer(lyr) {
  map.add(lyr);
}

I encourage you to read this post about writing Arcade expressions for visualizations in ArcGIS Online. It demonstrates how to leverage the Arcade live editor, which provides a mechanism for writing, testing, and debugging Arcade scripts. In fact, that came in handy a couple of times while writing the scripts referenced in this post.

Now try it out on your own data! Write a new Arcade expression in ArcGIS Online, save the layer as a new item, and load the layer or web map item to a custom app and see how this workflow can significantly simplify the development experience.

Other resources

Arcade is supported in both versions 4.2 and 3.19 of the ArcGIS API for JavaScript, although with some differences. Version 4.2 supports Arcade for visualization only. Version 3.19 fully supports Arcade in labeling and visualization contexts, including functions for generating renderers via the smartMapping module.

Check out the following resources when learning how to use Arcade in the JavaScript API:

Share this article