Popup charts for point clusters

This sample demonstrates how to summarize clustered features by type using charts in a cluster's popup. The app visualizes power plants as point clusters. Each cluster's popup displays a list of the five most common fuel types used to generate power within the cluster. It also includes two pie charts that summarize the number of power plants by fuel type and the total mW capacity for each fuel type within the cluster.

The list and charts are created using ExpressionContent popup elements, which can be used to conditionally build rich text, charts, or field lists with Arcade expressions. Expressions that return popup elements must return a dictionary representing the web map specification of the desired content element. The following popup elements are supported: TextContent, FieldsContent, and MediaContent.

Create an HTML list with Arcade

To return HTML from an Arcade expression, the HTML must be returned as a TextContent element. Expressions for building a text content element must return a dictionary matching the following specification:

Use dark colors for code blocksCopy
1
2
3
4
return {
  type: "text",
  text: "<b>The text to display in the popup</b>"
}

Because TextContent can contain rich text, we can dynamically build HTML elements as a text value within an Arcade expression. Note that you cannot return HTML in Arcade in any other case, such as displaying an expression value from the PopupTemplate.expressionInfos property.

The following expression demonstrates how this works. Read the comments for details.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Inform the popup, which fields the expression expects to use
// to ensure they are downloaded to the client
Expects($aggregatedFeatures, "fuel1", "capacity_mw")

// Get the total number of features in the cluster
// grouped by type and store it in a num_features field
var statsFS = GroupBy($aggregatedFeatures,
  [
    { name: 'Type', expression: 'fuel1'},
  ],
  [
    { name: 'num_features', expression: '1', statistic: 'COUNT' }
  ]
);
// Only return the top 5 results ordered by total features
var ordered = Top(OrderBy(statsFs, 'num_features DESC'), 5);

// create an HTML ordered list as a string
var list = "<ol>";

// Create a new list item for each type in the top 5 'ordered' featureset
for (var group in ordered){
  list += \`<li>\${group.Type} (\${Text(group.num_features, "#,###")})</li>\`
}
list += "</ol>";

// return the list as rich text
return {
  type: "text",
  text: list
}

Create pie charts with Arcade

Expressions for charts must return a dictionary matching the web map specification of a MediaContent element:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
return {
  type: "media",
  attributes: {
    field1: number,
    field2: number
  },
  title: "Media content title",
  mediaInfos: [
    {
      type: "piechart",  // can also be "columnchart", "linechart", "piechart", "image"
      title: "Chart title",
      value: {
        // the list of attribute values to include in the chart
        fields: [ "field1", "field2" ],
        // optional array of RGBA color arrays to set custom chart colors
        colors: [[R,G,B],[R,G,B]]
      }
    }
  // you can define more charts here
  ]
}

When implemented in JavaScript, the charts can be defined with a list of field names. However, when dynamically creating charts with Arcade, you must create an attributes dictionary that stores key-value pairs containing the data to use in the chart. The keys are the field names to reference in the fields property of the chart value.

The following expression demonstrates how this works by creating two pie charts. Read the comments for details.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Inform the popup, which fields the expression expects to use
// to ensure they are downloaded to the client.
Expects($aggregatedFeatures, "fuel1", "capacity_mw");
// Create an object to store attributes
// and arrays used to store field names for each chart.
var attributes = {};
var countFieldNames = [];
var capacityFieldNames = [];
// Create an array of the power plant types in the
// order of the renderer.
// Create an object of power plant types and
// respective renderer colors.
var rendererColors = {
  Hydro: [166,206,227],
  Solar: [178,223,138],
  Wind: [51,160,44],
  Gas: [251,154,153],
  Oil: [227,26,28],
  Coal: [253,191,111],
  Biomass: [255,127,0],
  Waste: [202,178,214],
  Nuclear: [106,61,154],
  Geothermal: [31,120,180]
};

// Create an array to hold the custom chart colors.
var chartColors = [];

// Get the distinct types of fuel within the cluster.
// This can be an array of 1-12 types depending on the cluster.
var types = Distinct($aggregatedFeatures, "fuel1");

// Add attributes to the dictionary for each type
// one for the total count, the other for capacity
for(var t in types){
  var type = t.fuel1;
  // contains all the features belonging to the type
  // on this iteration of the loop
  var filtered = Filter($aggregatedFeatures, "fuel1 = @type");

  // Get the renderer color for the current type and if it doesn't exist,
  // set the color to grey for "Other".
  var color = iif(hasValue(rendererColors, type), rendererColors[type], [128,128,128,255]);

  // Create a field name for the count of the current type.
  var countFieldName = type + " count";
  // Store the total number of features in the attribute
  attributes[countFieldName] = Count(filtered);

  // Repeat this process for the second chart.
  var capacityFieldName = type + " capacity (mW)";
  // Instead of count, get the sum for the capacity and add to attributes.
  attributes[capacityFieldName] = DefaultValue(Sum(filtered, "capacity_mw"), 0);

  // Push the field names into the appropriate arrays.
  Push(countFieldNames, countFieldName);
  Push(capacityFieldNames, capacityFieldName);
  // Push the chart color for the field into the chartColors array.
  Push(chartColors, color);
}

return {
  type: "media",
  // attributes for both charts here
  attributes: attributes,
  title: "Count vs. capacity comparison",
  // list keys for attributes for each chart in "value"
  mediaInfos: [{
    type: "piechart",
    title: "Total count",
    value: {
      fields: countFieldNames,
      colors: chartColors
    }
  }, {
    type: "piechart",
    title: "Total capacity (mW)",
    value: {
      fields: capacityFieldNames,
      colors: chartColors
    }
  }]
}

The expressions are added directly to an ExpressionContent element within the popupTemplate content.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
layer.featureReduction = {
  type: "cluster",
  popupTemplate: {
    title: "Power plant summary",
    content: [{
      type: "expression",
      expressionInfo: "// list expression here",
      title: "List of fuel types"
    }, {
      type: "expression",
      expressionInfo: {
        expression: "// chart expression here"
        title: "Pie charts"
      }
    }]
  }
};
Image preview of related sample Intro to clustering

Intro to clustering

Intro to clustering

Image preview of related sample Clustering - generate suggested configuration

Clustering - generate suggested configuration

Clustering - generate suggested configuration

Image preview of related sample Clustering - filter popup features

Clustering - filter popup features

This sample demonstrates how to filter clustered features within a cluster's popup.

Image preview of related sample Clustering - query clusters

Clustering - query clusters

Clustering - query clusters

Image preview of related sample Clustering - advanced configuration

Clustering - advanced configuration

Clustering - advanced configuration

ExpressionContent

Read the Core API Reference for more information.

FeatureReductionCluster

Read the Core API Reference for more information.

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.