Install
$ agentstack add skill-saschabrunnerch-arcgis-maps-sdk-js-ai-context-arcgis-arcade ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
ArcGIS Arcade Expressions
Use this skill for writing Arcade expressions for popups, renderers, labels, and calculations.
Import Patterns
Direct ESM Imports
import * as arcade from "@arcgis/core/arcade.js";
Dynamic Imports (CDN)
const arcade = await $arcgis.import("@arcgis/core/arcade.js");
Arcade Basics
Arcade is an expression language for ArcGIS. It's used for:
- Dynamic popup content
- Data-driven rendering
- Custom labels
- Field calculations
- Form validation
Basic Syntax
// Variables
var population = $feature.population;
var area = $feature.area_sqkm;
// Calculations
var density = population / area;
// Return result
return Round(density, 2);
Arcade in PopupTemplates
Expression Infos
const popupTemplate = {
title: "{name}",
expressionInfos: [
{
name: "population-density",
title: "Population Density",
expression: "Round($feature.population / $feature.area_sqkm, 2)",
},
{
name: "formatted-date",
title: "Formatted Date",
expression: "Text($feature.created_date, 'MMMM D, YYYY')",
},
],
content: "Density: {expression/population-density} people/km²",
};
Complex Expressions
const popupTemplate = {
title: "{name}",
expressionInfos: [
{
name: "predominant-category",
title: "Predominant Category",
expression: `
var fields = [
{ value: $feature.category_a, alias: "Category A" },
{ value: $feature.category_b, alias: "Category B" },
{ value: $feature.category_c, alias: "Category C" }
];
var maxValue = -Infinity;
var maxCategory = "";
for (var i in fields) {
if (fields[i].value > maxValue) {
maxValue = fields[i].value;
maxCategory = fields[i].alias;
}
}
return maxCategory;
`,
},
],
content: [
{
type: "text",
text: "The predominant category is: {expression/predominant-category}",
},
{
type: "fields",
fieldInfos: [
{
fieldName: "expression/predominant-category",
},
],
},
],
};
Arcade in Renderers
Value Expression
const renderer = {
type: "unique-value",
valueExpression: `
var labor = $feature.labor_force;
var notLabor = $feature.not_in_labor_force;
if (labor > notLabor) {
return "In labor force";
} else {
return "Not in labor force";
}
`,
valueExpressionTitle: "Labor Force Status",
uniqueValueInfos: [
{
value: "In labor force",
symbol: { type: "simple-fill", color: "blue" },
},
{
value: "Not in labor force",
symbol: { type: "simple-fill", color: "orange" },
},
],
};
Visual Variable Expression
const renderer = {
type: "simple",
symbol: { type: "simple-marker", color: "red" },
visualVariables: [
{
type: "size",
valueExpression: "Sqrt($feature.population) * 0.1",
valueExpressionTitle: "Population (scaled)",
stops: [
{ value: 10, size: 4 },
{ value: 100, size: 40 },
],
},
{
type: "opacity",
valueExpression: "($feature.value / $feature.max_value) * 100",
valueExpressionTitle: "Percentage of max",
stops: [
{ value: 20, opacity: 0.2 },
{ value: 80, opacity: 1 },
],
},
],
};
Arcade in Labels
layer.labelingInfo = [
{
symbol: {
type: "text",
color: "black",
font: { size: 10 },
},
labelExpressionInfo: {
expression: `
var name = $feature.name;
var pop = $feature.population;
if (pop > 1000000) {
return name + " (" + Round(pop/1000000, 1) + "M)";
} else if (pop > 1000) {
return name + " (" + Round(pop/1000, 0) + "K)";
}
return name;
`,
},
where: "population > 50000",
},
];
Common Arcade Functions
Math Functions
Round(3.14159, 2) // 3.14
Floor(3.9) // 3
Ceil(3.1) // 4
Abs(-5) // 5
Sqrt(16) // 4
Pow(2, 3) // 8
Min(1, 2, 3) // 1
Max(1, 2, 3) // 3
Sum([1, 2, 3]) // 6
Mean([1, 2, 3]) // 2
Text Functions
Upper("hello") // "HELLO"
Lower("HELLO") // "hello"
Trim(" hello ") // "hello"
Left("hello", 2) // "he"
Right("hello", 2) // "lo"
Mid("hello", 2, 2) // "ll"
Find("l", "hello") // 2
Replace("hello", "l", "L") // "heLLo"
Split("a,b,c", ",") // ["a", "b", "c"]
Concatenate(["a", "b"]) // "ab"
Date Functions
Now() // Current date/time
Today() // Current date
Year($feature.date_field) // Extract year
Month($feature.date_field) // Extract month (1-12)
Day($feature.date_field) // Extract day
DateDiff(Now(), $feature.date, "days") // Days between dates
Text($feature.date, "MMMM D, YYYY") // Format date
Geometry Functions
Area($feature, "square-kilometers")
Length($feature, "kilometers")
Centroid($feature)
Buffer($feature, 100, "meters")
Intersects($feature, $otherFeature)
Contains($feature, $point)
Conditional Functions
// IIf (inline if)
IIf($feature.value > 100, "High", "Low")
// When (multiple conditions)
When(
$feature.type == "A", "Type A",
$feature.type == "B", "Type B",
"Other"
)
// Decode (value matching)
Decode($feature.code,
1, "One",
2, "Two",
3, "Three",
"Unknown"
)
Array Functions
var arr = [1, 2, 3, 4, 5];
Count(arr) // 5
First(arr) // 1
Last(arr) // 5
IndexOf(arr, 3) // 2
Includes(arr, 3) // true
Push(arr, 6) // [1, 2, 3, 4, 5, 6]
Reverse(arr) // [5, 4, 3, 2, 1]
Sort(arr) // [1, 2, 3, 4, 5]
Slice(arr, 1, 3) // [2, 3]
Feature Access
// Current feature
$feature.fieldName
// All features in layer (for aggregation)
var allFeatures = FeatureSet($layer);
var filtered = Filter(allFeatures, "type = 'A'");
var total = Sum(filtered, "value");
// Related records
var related = FeatureSetByRelationshipName($feature, "relationshipName");
// Global variables
$map // Reference to map
$view // Reference to view
$datastore // Reference to data store
Execute Arcade Programmatically
import * as arcade from "@arcgis/core/arcade.js";
// Create profile
const profile = {
variables: [
{
name: "$feature",
type: "feature",
},
],
};
// Compile expression
const executor = await arcade.createArcadeExecutor(
"Round($feature.value * 100, 2)",
profile,
);
// Execute with feature
const result = await executor.executeAsync({
$feature: graphic,
});
console.log("Result:", result);
> Note: In v5.0, ExecuteContext.lruCache is deprecated. Use ExecuteContext.cache instead.
Arcade in HTML (Script Tags)
var total = $feature.value_a + $feature.value_b;
var percentage = Round((total / $feature.max_value) * 100, 1);
return percentage + "%";
const expression = document.getElementById("my-expression").text;
const popupTemplate = {
expressionInfos: [
{
name: "my-calc",
expression: expression,
},
],
content: "Value: {expression/my-calc}",
};
Arcade Expression Profiles
Different Arcade profiles expose different global variables and functions:
| Profile | Global Variables | Used For | | ------------------ | ------------------------------------------ | --------------------------- | | popup | $feature, $layer, $map, $datastore | Popup content | | visualization | $feature, $view | Renderers, visual variables | | labeling | $feature, $view | Label expressions | | field-calculate | $feature, $layer | Field calculations | | form-calculation | $feature, $layer, $originalFeature | Form calculated expressions | | constraint | $feature, $layer, $originalFeature | Form validation | | alias | $feature, $layer | Cluster popup aliases |
Reference Samples
popuptemplate-arcade- Arcade expressions in PopupTemplatespopuptemplate-arcade-expression-content- Arcade expression content in popupsvisualization-arcade- Arcade-driven visualizationarcade-execute-chart- Execute Arcade with charting
Common Pitfalls
- Null values: Always check for nulls with
IsEmpty($feature.field)before arithmetic operations. Dividing by null or adding to null produces null.
``arcade // Anti-pattern: no null check return $feature.population / $feature.area; ``
``arcade // Correct: check for null if (IsEmpty($feature.area) || $feature.area == 0) { return "N/A"; } return Round($feature.population / $feature.area, 2); ``
- Type coercion: Use
Number()orText()for explicit conversion when mixing types.
- Case sensitivity: Arcade function names are case-insensitive, but field names must match the source data exactly.
- Performance: Complex expressions in renderers and labels evaluate per feature per frame. Keep them simple for large datasets.
- Debugging: Use the
Console()function to debug expressions. Output appears in the browser's developer console.
- FeatureSet queries:
FeatureSet()andFilter()execute server-side queries. They can be slow in popup expressions if the layer has many features.
Related Skills
- See
arcgis-popup-templatesfor popup template configuration - See
arcgis-visualizationfor renderer and symbol configuration - See
arcgis-smart-mappingfor data-driven visualization - See
arcgis-coding-componentsfor the `` component
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: SaschaBrunnerCH
- Source: SaschaBrunnerCH/arcgis-maps-sdk-js-ai-context
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.