Skip to content

Clustering groups points into clusters based on their spatial proximity to one another. Clusters can reveal information that is difficult to visualize when there are hundreds or thousands of points in a map that may overlap or cover each other. Adopt clustering if you want to discover patterns in your data, such as:

  • Identify where students live in a city so that the local government can plan appropriate educational and accommodation resources. Clustering allows you to visualize this data without revealing the individual student addresses.
  • Visualize the most common type of environmental complaints that are registered across a city so that authorities can plan how to deploy their environmental officers.
  • Ensure that police units are evenly spread throughout city so that a rapid response time can be maintained for emergency calls from the public.

In the map below, for example, various power plants of north-west Europe are clustered together so that you can easily see the count of power plants in each region. The size of each cluster is proportional to the number of points in each cluster and is recalculated when the map is zoomed.

Clustering

Clustering can be defined on a point feature layer authored and published using the ArcGIS Online Map Viewer or ArcGIS Pro. If you add a feature layer from an ArcGIS feature service, web map, or mobile map package to your app, any predefined clustering displays automatically.

If the published point feature layer is not enabled with clustering, or if you want to cluster point graphics, you can use this SDK to define clustering by following the steps detailed in Enable clustering. You can enhance the visual representation of the clusters and display summary statistics of the clustered point attributes (not just the number of points) by using a variety of more complex renderers, labels, and pop-ups, as described in Enhanced statistics and renderers.

Enable clustering

To cluster point-based features or graphics in your app, you need to create a ClusteringFeatureReduction object that takes a renderer. The renderer determines the cluster's color and shape, but the cluster's size is determined by the number of points in the cluster. So, a cluster with more points is bigger than a cluster with fewer points. You can cluster point features or graphics as follows:

  1. Create a SimpleRenderer using a SimpleMarkerSymbol.
  2. Create a ClusteringFeatureReduction using the simple renderer.
  3. Pass the ClusteringFeatureReduction to the FeatureLayer.featureReduction or the GraphicsOverlay.featureReduction.
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    final simpleMarkerSymbol = SimpleMarkerSymbol(color: Colors.teal);

    final simpleRenderer = SimpleRenderer(symbol: simpleMarkerSymbol);

    final clusteringFeatureReduction =
        ClusteringFeatureReduction(simpleRenderer);

    featureLayer.featureReduction = clusteringFeatureReduction;
    // Alternatively, set the feature reduction on the graphics overlay.
    // (Note: FeatureReduction instances can't be shared. The line below would result in an
    // "object already owned" exception since it's already been applied to the FeatureLayer.)
    // graphicsOverlay.featureReduction = clusteringFeatureReduction;

The map below contains a single point symbol for the 8000+ power plants in the United States. On the right side, the points are aggregated into clusters using the default clustering values. Every time the mapview's viewpoint changes the clusters may be recalculated and their symbols redrawn.

Clustering.

Label clusters

The number of points in the cluster is stored in a field called "cluster_count" and is updated every time the cluster redraws. You can display this information in the clusters label, as follows:

  1. Create a LabelDefinition that uses a SimpleLabelExpression of "[cluster_count]" and a black TextSymbol.
  2. Set label definition placement.
  3. Add the label definition to the ClusteringFeatureReduction.
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    final labelDefinition = LabelDefinition(
      labelExpression:
          SimpleLabelExpression(simpleExpression: '[cluster_count]'),
      textSymbol: TextSymbol(color: Colors.black, size: 15),
    );

    labelDefinition.placement = LabelingPlacement.pointCenterCenter;

    clusteringFeatureReduction.labelDefinitions.add(labelDefinition);

The map below shows clusters labeled with a SimpleLabelExpression.

Clustering.

Configure clusters

The process of clustering groups points into clusters based on an area of influence defined in screen space by the ClusteringFeatureReduction.radius. This radius has a default value which is 60 device-independent pixels (dpi), but you can set it to a higher value to aggregate points from a larger area or to a lower value to aggregate points from a smaller area. The size of each cluster symbol on the map is proportional to the number of features within the cluster and is recalculated every time the viewpoint changes.

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    clusteringFeatureReduction.radius = 120;

The map below shows points clustered with a clustering radius of 120.

Clustering.

Enhanced statistics and renderers

You can enhance the information displayed to your users by presenting statistical information, such as the average, max, and mode value of the cluster's point attributes. First, you need to aggregate, or summarize, the cluster's point data, and then you can:

  • Apply advanced renderers, such as class break and unique value renderers.
  • Configure labels that use simple or Arcade expressions.
  • Design user friendly pop-up text.

Aggregate the point data

First, examine the attributes in your feature layer or graphics overlay and decide which attributes you want to summarize. Next, specify how the attribute values are summarized and store the results in an aggregate field, as follows:

  1. Create a new AggregateField for each statistical operation you want to perform on an attribute. Give it a name.
  2. Set the statisticFieldName as the attribute (from the feature layer or graphic overlay) you want to analyze.
  3. State the AggregateStatisticType, such as count, average, mode, and max, that will be applied to the statisticFieldName.
  4. Add a collection of your aggregate fields to your ClusteringFeatureReduction so they can be used by its renderer, label, and pop-up definitions.

The code below creates and adds 3 AggregateField objects to the ClusteringFeatureReduction. In the first one, for example, the value of the "fuel1" field that appears most often (mode value) is stored in the "most_common_fuel" aggregate field, the average value of the "capacity_mw" field is stored in the "average_capacity_mw" aggregate field and so on.

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    clusteringFeatureReduction.aggregateFields.addAll([
      AggregateField.withFieldName(
        name: 'most_common_fuel',
        statisticFieldName: 'fuel1',
        statisticType: AggregateStatisticType.mode,
      ),
      AggregateField.withFieldName(
        name: 'average_capacity_mw',
        statisticFieldName: 'capacity_mw',
        statisticType: AggregateStatisticType.average,
      ),
      AggregateField.withFieldName(
        name: 'total_capacity_mw',
        statisticFieldName: 'capacity_mw',
        statisticType: AggregateStatisticType.sum,
      ),
    ]);

Apply a class breaks renderer

You can apply a renderer to the cluster to display the value of the aggregate fields. Class breaks renderers are useful if you want to apply different colors to ranges of values. You can apply a class breaks renderer to the cluster as follows:

  1. For each range of values, create a ClassBreak and assign a SimpleMarkerSymbol with a unique color.
  2. Create a ClassBreaksRenderer for the aggregate field and provide the collection of class breaks.
  3. Pass the class breaks renderer to the constructor of the ClusteringFeatureReduction and pass that to FeatureLayer.featureReduction or GraphicsOverlay.featureReduction.
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    final classBreak1 = ClassBreak(
      description: '<5000',
      label: '<5000',
      minValue: 0,
      maxValue: 5000,
      symbol: SimpleMarkerSymbol(color: Colors.cyan),
      alternateSymbols: [SimpleMarkerSymbol(color: Colors.pink)],
    );

    final classBreak2 = ClassBreak(
      description: '>5000',
      label: '>5000',
      minValue: 5001,
      maxValue: 10000,
      symbol: SimpleMarkerSymbol(color: Colors.greenAccent),
      alternateSymbols: [SimpleMarkerSymbol(color: Colors.pink)],
    );

    final classBreak3 = ClassBreak(
      description: '>10000',
      label: '>10000',
      minValue: 10001,
      maxValue: 15000,
      symbol: SimpleMarkerSymbol(color: Colors.green),
      alternateSymbols: [SimpleMarkerSymbol(color: Colors.pink)],
    );

    final classBreak4 = ClassBreak(
      description: '>15000',
      label: '>15000',
      minValue: 15001,
      maxValue: 50000,
      symbol: SimpleMarkerSymbol(color: Colors.orange),
      alternateSymbols: [SimpleMarkerSymbol(color: Colors.pink)],
    );

    final classBreak5 = ClassBreak(
      description: '>50000',
      label: '>50000',
      minValue: 50001,
      maxValue: 1000000,
      symbol: SimpleMarkerSymbol(color: Colors.red),
      alternateSymbols: [SimpleMarkerSymbol(color: Colors.pink)],
    );

The map below shows the clusters rendered with a class breaks renderer and reflects the range of total_capacity_mw values. For example, the red color indicates that the sum of the capacity_mw of all the points in a cluster is greater than 50000 MW.

Clustering.

Configure label expressions

As shown previously, you can label each cluster with the number of points in the cluster. However, now that you have created some aggregate fields, you can use these to label the clusters by configuring a SimpleLabelExpression or an ArcadeLabelExpression.

In the map below, the most_common_fuel aggregate field value of each cluster is displayed in a label by using a SimpleLabelExpression.

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    final clusteringFeatureReduction =
        ClusteringFeatureReduction(classBreaksRenderer);

    final commonFuelLabelExpression =
        SimpleLabelExpression(simpleExpression: '[most_common_fuel]');
Clustering.

In the map below, the total_capacity_mw aggregate field value of each cluster is reformated, concatenated with the "MW", and displayed in a label by using an ArcadeLabelExpression.

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    final labelExpression = ArcadeLabelExpression(
      arcadeString:
          "Concatenate([Text(\$feature.total_capacity_mw, '###,###') + ' MW'])",
    );
Clustering.

Configure pop-up expressions

You can configure your app to display a pop-up when the user clicks on a cluster. By default, the cluster's pop-up displays its "point_count" value; the number of points in the cluster. If the ClusteringFeatureReduction has aggregate fields, the cluster pop-up will display the aggregate field values. However, if you want to present a more user-friendly text, you can create an Arcade expression, as follows:

  1. Create a PopupDefinition using the ClusteringFeatureReduction.
  2. Assign the pop-up definition to the clustering feature reduction.
  3. Specify a title and remove all elements from the pop-up definition.
  4. Create a PopupExpression, provide an Arcade expression, and specify that the return type is string. In this example, the Arcade expression reformats and concatenates a few aggregate fields representing the average and total capacities, along with some helpful text.
  5. Create an ExpressionPopupElement using the pop-up expression.
  6. Add the expression pop-up element to the pop-up definition's collection of elements.
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    final popupDefinition =
        PopupDefinition.withPopupSource(clusteringFeatureReduction);

    clusteringFeatureReduction.popupDefinition = popupDefinition;
    clusteringFeatureReduction.popupDefinition?.title = 'Cluster Summary';
    clusteringFeatureReduction.popupDefinition?.elements.clear();

    final popupExpression1 = PopupExpression();
    popupExpression1.expression = '''
var result = Concatenate(['The power plants in this cluster have an average capacity of ' + Text(\$feature.average_capacity_mw, '#.00') + ' megawatts and produce a total of ' + Text(\$feature.total_capacity_mw, '###,###.00') + ' megawatts of power.']);
return { type : 'text', text : result };
''';
    popupExpression1.returnType = PopupExpressionReturnType.string;

    final expressionPopupElement1 =
        ExpressionPopupElement(popupExpression: popupExpression1);

    final popupExpression2 = PopupExpression();
    popupExpression2.expression = '''
var result = Concatenate(['Most power plants in this cluster generate power from ' + \$feature.most_common_fuel + '.']);
return { type : 'text', text : result };
''';
    popupExpression2.returnType = PopupExpressionReturnType.string;

    final expressionPopupElement2 =
        ExpressionPopupElement(popupExpression: popupExpression2);

    clusteringFeatureReduction.popupDefinition?.elements
        .addAll([expressionPopupElement1, expressionPopupElement2]);

The map below shows a pop-up configured with a PopupExpression.

Clustering.

For more information about how to add pop-ups to your app, see the PopupView component and example code that is provided by the ArcGIS Maps SDK for Flutter Toolkit.

Explore a cluster

Each cluster of points is an aggregation of geoelements called an AggregateGeoElement. If you want to find out which geoelements are aggregated into an single cluster, retrieve the cluster's collection of geoelements by calling AggregateGeoElement.getGeoElements(). You can then iterate through the collection to display individual geoelement attribute values, for example.

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    // Get the selected cluster and obtain its collection of geoelements.
    if (aggregateGeoElement.isSelected) {
      final geoElements = await aggregateGeoElement.getGeoElements();

      if (geoElements.isEmpty) return;

      // If you have mix of feature layers and graphics overlays in your map,
      // check for the type of geoelement to be a feature or graphic, for example.
      for (final geoElement in geoElements) {
        if (geoElement is Feature) {
          final geoElementName = geoElement.attributes['name'] as String;

          debugPrint(geoElementName);
        }
      }
    }

Samples

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