This sample demonstrates how to enable clustering on a FeatureLayer and aggregate numeric attributes to use in a custom cluster renderer. This app aggregates car crash data into clusters and styles each cluster with a red color if it includes at least one fatality. Clusters with no fatalities are colored beige.
Clustering is a method of aggregating features in a FeatureLayer, CSVLayer, GeoJSONLayer, WFSLayer, or OGCFeatureLayer by grouping them in clusters defined by screen space.
Aggregate fields are defined in the fields property of the FeatureReductionCluster class. Once defined, these fields may be used in the renderer, labels, and popup of the clusters.
The lines highlighted in green in the snippet below show how aggregate fields are defined. Lines highlighted in blue show where these fields are used.
const clusterConfig = { type: "cluster",
fields: [ new AggregateField({ name: "aggregateCount", statisticType: "count", }), new AggregateField({ name: "SUM_PERSONS_INJURED", onStatisticField: "NUMBER_OF_PERSONS_INJURED", statisticType: "sum", }), new AggregateField({ name: "SUM_PEDESTRIANS_INJURED", onStatisticField: "NUMBER_OF_PEDESTRIANS_INJURED", statisticType: "sum", }), new AggregateField({ name: "SUM_CYCLIST_INJURED", onStatisticField: "NUMBER_OF_CYCLIST_INJURED", statisticType: "sum", }), new AggregateField({ name: "SUM_MOTORIST_INJURED", onStatisticField: "NUMBER_OF_MOTORIST_INJURED", statisticType: "sum", }), new AggregateField({ name: "AVG_MOTORIST_INJURED", onStatisticField: "NUMBER_OF_MOTORIST_INJURED", statisticType: "avg", }), new AggregateField({ name: "SUM_KILLED", onStatisticField: "NUMBER_OF_PERSONS_KILLED", statisticType: "sum", }),
], renderer: { type: "class-breaks", field: "SUM_KILLED", legendOptions: { title: "Car crashes", }, classBreakInfos: [ { minValue: 0, maxValue: 0, label: "No fatalities", symbol: { type: "simple-marker", color: excludedColor, outline: null, outline: { color: "rgba(153, 31, 23, 0.3)", width: 0.3, }, }, }, { minValue: 1, maxValue: 99999999999, label: "Includes fatalities", symbol: { type: "simple-marker", color: includedColor, outline: { color: "rgba(153, 31, 23, 0.3)", width: 0.3, }, }, }, ], }, clusterRadius: "100px", fieldConfigurations: [ { name: "aggregateCount", alias: "Total crashes", fieldFormat: { type: "number", maximumFractionDigits: 0, minimumFractionDigits: 0, useGrouping: "always", }, }, { name: "SUM_PERSONS_INJURED", alias: "Total injured", fieldFormat: { type: "number", maximumFractionDigits: 0, minimumFractionDigits: 0, useGrouping: "never", }, }, { name: "SUM_PEDESTRIANS_INJURED", alias: "Pedestrians injured", }, { name: "SUM_CYCLIST_INJURED", alias: "Cyclists injured", }, { name: "SUM_MOTORIST_INJURED", alias: "Motorists injured", }, { name: "SUM_KILLED", alias: "Total killed", }, ], popupTemplate: { title: "Crash summary", content: [ { type: "text", text: "{aggregateCount} car crashes occurred in this area, including {SUM_KILLED} fatalities.", }, { type: "fields", fieldInfos: [ { fieldName: "aggregateCount", }, { fieldName: "SUM_PERSONS_INJURED", }, { fieldName: "SUM_PEDESTRIANS_INJURED", }, { fieldName: "SUM_CYCLIST_INJURED", }, { fieldName: "SUM_MOTORIST_INJURED", }, { fieldName: "SUM_KILLED", }, ], }, ], }, clusterMinSize: "24px", clusterMaxSize: "60px", labelingInfo: [ { deconflictionStrategy: "none", labelExpressionInfo: { expression: ` var label = Text($feature.aggregateCount, '#,###'); var fatalities = $feature.SUM_KILLED; if(fatalities > 0){ label += \`\n(\${fatalities})\` } return label; `, }, symbol: { type: "text", color: "white", font: { weight: "bold", family: "Noto Sans", size: "12px", }, haloColor: includedColor, haloSize: 1, }, labelPlacement: "center-center", }, ], };