This application displays 2010 population density by census tracts. The population is queried by age and gender among census tracts intersecting a buffer, and is displayed in a population pyramid chart.
How it works
The population pyramid chart is updated as the user moves the buffer by dragging its the center point or resizes the buffer by moving its edge point. As the user moves one of the two points, a geodesic buffer is recalculated and used to query the statistics of all features in the layer view that intersect the buffer. The center and edge points of the buffer graphic are updated by listening to the update event on SketchViewModel.
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
// Listen to SketchViewModel's update event so that population pyramid chart// is updated as the graphics are updatedsketchViewModel.on("update", (event) => {
// If the edge graphic is moving, keep the center graphic// at its initial location. Only move edge graphicif (
event.toolEventInfo && event.toolEventInfo.mover.attributes.edge
) {
const toolType = event.toolEventInfo.type;
if (toolType === "move-start") {
centerGeometryAtStart = centerGraphic.geometry;
}
// keep the center graphic at its initial location when edge point is movingelse {
centerGraphic.geometry = centerGeometryAtStart;
}
}
// the center or edge graphic is being moved, recalculate the bufferconst vertices = [
[centerGraphic.geometry.x, centerGraphic.geometry.y],
[edgeGraphic.geometry.x, edgeGraphic.geometry.y]
debounceOnMove(vertices).catch((error) => {
if (!promiseUtils.isAbortError(error)) {
throw error;
}
});
if (event.state == "complete") {
sketchViewModel.update([edgeGraphic, centerGraphic], {
tool: "move" });
}
});
/*********************************************************************
* Edge or center graphics are being moved. Recalculate the buffer with
* updated geometry information and run the query stats again.
*********************************************************************/const debounceOnMove = promiseUtils.debounce(async (vertices) => {
// client-side stats query of features that intersect the bufferconst buffer = await calculateBuffer(vertices);
// Query female and male age groups of the census tracts that intersect// the buffer polygon on the clientconst newData = await queryLayerViewAgeStats(buffer);
updateChart(newData);
// user is clicking on the view... call update method with the center and edge graphicsif (event.state === "complete") {
sketchViewModel.update([edgeGraphic, centerGraphic], {
tool: "move" });
}
});
The updated buffer polygon is then used to query statistics to get total population belonging to different age categories by gender among census tracts. Once the statistics query results are returned, the information is displayed in the population pyramid chart.
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
/*********************************************************************
* Spatial query the census tracts feature layer view for statistics
* using the updated buffer polygon.
*********************************************************************/functionqueryLayerViewAgeStats(buffer) {
// Data storage for the chartlet femaleAgeData = [],
maleAgeData = [];
// Client-side spatial query:// Get a sum of age groups for census tracts that intersect the polygon bufferconst query = featureLayerView.layer.createQuery();
query.outStatistics = statDefinitions;
query.geometry = buffer;
// Query the features on the client using FeatureLayerView.queryFeaturesreturn featureLayerView
.queryFeatures(query)
.then((results) => {
// Statistics query returns a feature with 'stats' as attributesconst attributes = results.features[0].attributes;
// Loop through attributes and save the values for use in the population pyramid.for (const key in attributes) {
if (key.includes("FEM")) {
femaleAgeData.push(attributes[key]);
} else {
// Make 'all male age group population' total negative so that// data will be displayed to the left of female age group maleAgeData.push(-Math.abs(attributes[key]));
}
}
// Return information, separated by genderreturn [femaleAgeData, maleAgeData];
})
.catch((error) => {
console.error(error);
});
}