Skip to content

Getting data items within cursor selection

This tutorial will show how we can grab data items that fall within the selection of a cursor on a scatter (XYChart) chart.

Our chart setup will use a ValueAxis as its Y-axis, and a DateAxis for its X-axis, just so we can look at how we can extract selection boundaries for both types of axes.

Our chart cursor’s behavior will be set to "selectXY".

We’ll use cursor’s "selectended" event to grab data items within the selection.

cursor.events.on("selectended", function(ev) {
// Get actors
let cursor = ev.target;
// Get selection boundaries
let x1 = xAxis.positionToDate(
xAxis.toAxisPosition(cursor.getPrivate("downPositionX"))
).getTime();
let x2 = xAxis.positionToDate(
xAxis.toAxisPosition(cursor.getPrivate("positionX"))
).getTime();
let y1 = yAxis.positionToValue(
yAxis.toAxisPosition(cursor.getPrivate("downPositionY"))
);
let y2 = yAxis.positionToValue(
yAxis.toAxisPosition(cursor.getPrivate("positionY"))
);
// Account for centering of bullets on a DateAxis
let baseInterval = xAxis.getPrivate("baseInterval");
let baseDuration = am5.time.getDuration(baseInterval.timeUnit, baseInterval.count) * series.get("locationX");
x1 -= baseDuration;
x2 -= baseDuration;
// Assemble bounds
let bounds = {
left: x1 > x2 ? x2 : x1,
right: x1 > x2 ? x1 : x2,
top: y1 < y2 ? y1 : y2,
bottom: y1 < y2 ? y2 : y1
};
// Filter data items within boundaries
let results = [];
am5.array.each(series.dataItems, function(dataItem) {
let x = dataItem.get("valueX");
let y = dataItem.get("valueY");
if (am5.math.inBounds({ x: x, y: y }, bounds)) {
results.push(dataItem);
}
});
// Results
console.log(results)
});

Let’s look at the code above.

To retrieve boundaries of the selection, we grab cursor’s private (read-only) settings: downPositionX and downPositionY (those hold relative position of where selection started), and positionX and positionY (that indicate position of the end of selection).

We convert those positions to positions on the respective axes using their toAxisPosition() methods.

This step is needed to convert the positions in cases chart is zoomed.

Finally, we convert X positions to actual dates using date axis’ positionToDate(), and Y positions to positionToValue() methods.

Before we can use the boundaries, there’s one crucial step left.

Date axis would make series center its bullets within its base interval. E.g. so the bullet that has timestamp of a start of the day, would be centered within the day.

The following code takes that into account when calculating final boundaries.

// Account for centering of bullets on a DateAxis
let baseInterval = xAxis.getPrivate("baseInterval");
let baseDuration = am5.time.getDuration(baseInterval.timeUnit, baseInterval.count) * series.get("locationX");
x1 -= baseDuration;
x2 -= baseDuration;

All that’s left is to iterate through series’ data items and check whether they fall within selection boundaries.

We can use amCharts 5 built-in array function am5.array.each() to iterate through series data items, as well as am5.math.inBounds() to quickly check whether data item is within the boundaries.

Open in CodePen