<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Timestamp-offset field | Sample | ArcGIS Maps SDK for JavaScript</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.3.3/chart.umd.min.js"></script>
<!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
<script type="module" src="https://js.arcgis.com/5.0/"></script>
--calcite-shell-panel-min-width: 420px;
--calcite-shell-panel-width: 420px;
--calcite-shell-panel-max-width: 80%;
--calcite-color-border-1: var(--calcite-color-brand);
margin: 1rem auto 0.5rem;
<calcite-shell class="custom-example-theme">
<arcgis-map center="-100, 35" zoom="3" id="map-element">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-popup slot="popup"></arcgis-popup>
<calcite-shell-panel id="rightPanel" slot="panel-end" display-mode="float">
heading="Fatal Accidents in USA - 2021"
description="Explore accidents data">
<calcite-chip-group id="type-chips" selection-mode="single-persist">
<calcite-chip appearance="outline-fill" selected value="day">Daily</calcite-chip>
<calcite-chip appearance="outline-fill" value="week">Weekly</calcite-chip>
<calcite-chip appearance="outline-fill" value="month">Monthly</calcite-chip>
<calcite-block expanded id="chart-block" heading="Total accidents by time of day">
<canvas class="canvas" id="chart-day" height="200" width="400"></canvas>
style="display: none"></canvas>
style="display: none"></canvas>
heading="Click on the graph bar to see hourly chart"
<canvas class="canvas" id="chart-day-distribution" height="200" width="400"></canvas>
<calcite-notice slot="footer" open width="full" icon>
<span slot="title">Note</span>
<span slot="message">Click on the graph bars to highlight the data in the map</span>
const [Map, FeatureLayer] = await $arcgis.import([
"@arcgis/core/layers/FeatureLayer.js",
id: "3582b744bba84668b52a16b0b6942544",
const viewElement = document.querySelector("arcgis-map");
// Add fatal accidents of 2021 across USA
const layer = new FeatureLayer({
id: "14079796fcb04fc186f412bf7c8577d5",
let layerView = await viewElement.whenLayerView(layer);
// Prepare data for total accidents by time of day chart
// Query the layer for total accidents, grouped by hour of day
const hourResult = await runQuery("1=1", "extract(hour from tsodate)");
const hourData = hourResult.features?.map((f) => f.attributes["count"]) ?? [];
const hourLabels = hourResult.features?.map((f) => f.attributes["EXPR_1"]) ?? [];
// Create a bar chart showing total number of accidents by time of day
updateChart("chart-day", hourData, hourLabels, false, 3000);
// Query the layer for total accidents, grouped by month
const monthResult = await runQuery("1=1", "extract(month from tsodate)");
const monthData = monthResult.features?.map((f) => f.attributes["count"]) ?? [];
// Create a bar chart showing total number of accidents by months
updateChart("chart-month", monthData, monthLabels, false, 4000);
// Query the layer for total accidents, grouped by week days
const weekResult = await runQuery("1=1", "DAY_WEEK");
const weekData = weekResult.features?.map((f) => f.attributes["count"]) ?? [];
const weekLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
// Create a bar chart showing total number of accidents by days of week
updateChart("chart-week", weekData, weekLabels, false, 7000);
// Create an empty chart for displaying accidents by hours breakdown when user
// clicks on month or week chart bars
let dayDistributionChart = updateChart("chart-day-distribution", [], hourLabels, true, 500);
// Called at startup to query and group accidents by hour, weekday, and month
async function runQuery(where, groupStats) {
// Create a query object that honors the layer settings
const query = layer.createQuery();
// Define statistics to calculate count of accidents
outStatisticFieldName: "count",
query.groupByFieldsForStatistics = [groupStats];
query.orderByFields = [groupStats];
const result = await layer.queryFeatures(query);
// Tracks the index of the currently selected bar in a chart.
// Used to highlight the selected bar and reset color when a new bar is clicked.
let previouslySelectedBarIndex = null;
// Handles chart bar clicks to highlight corresponding features on the map.
// Updates the layer view effect to visually filter accidents by selected time, weekday, or month.
async function applyFilterToAccidentsData(event, chart) {
const activePoints = chart.getElementsAtEventForMode(
if (!activePoints.length) return;
const idx = activePoints[0].index;
const label = chart.data.labels[idx];
// Reset previous selection if needed
if (previouslySelectedBarIndex >= 0) {
changeBarColor(chart, previouslySelectedBarIndex, "#007AC2");
if (previouslySelectedBarIndex === idx) {
previouslySelectedBarIndex = null;
layerView.featureEffect = undefined;
if (dayDistributionChart) {
dayDistributionChart.data.datasets[0].data = [];
dayDistributionChart.update();
dayChartBreakDownBlock.heading = "Click on the graph bar to see hourly chart";
// Highlight selected bar
changeBarColor(chart, idx, "red");
previouslySelectedBarIndex = idx;
if (event.target.id === "chart-day") {
where = `extract(hour from tsodate) = ${label}`;
} else if (event.target.id === "chart-month") {
const queryValue = monthLabels.indexOf(label) + 1;
where = `extract(month from tsodate) = ${queryValue}`;
title = `Accidents by days in ${label}`;
dayDistributionStats(where, "extract(day from tsodate)", title);
const queryValue = weekLabels.indexOf(label) + 1;
where = `DAY_WEEK = ${queryValue}`;
title = `Accidents by hours on ${label}`;
dayDistributionStats(where, "extract(hour from tsodate)", title);
layerView.featureEffect = {
excludedEffect: "blur(2px) opacity(0.2) grayscale(0.2)",
// Query and update the dayDistributionChart with breakdown stats when user clicks on a bar
async function dayDistributionStats(where, groupStats, label) {
const result = await runQuery(where, groupStats);
const features = result.features ?? [];
const chartData = features.map((f) => f.attributes["count"]);
const chartLabels = features.map((f) => f.attributes["EXPR_1"]);
if (dayChartBreakDownBlock.style.display === "none") {
dayChartBreakDownBlock.style.display = "block";
dayChartBreakDownBlock.heading = label;
dayDistributionChart.data.datasets[0].data = chartData;
dayDistributionChart.data.labels = chartLabels;
dayDistributionChart.data.datasets[0].backgroundColor = Array(chartData.length).fill(
dayDistributionChart.update();
// Updates the color of a chart bar when selected; called by applyFilterToAccidentsData
function changeBarColor(chart, index, color) {
chart.data.datasets[0].backgroundColor[index] = color;
// UI controls visible in the upper right panel
const chartChoiceControl = document.getElementById("type-chips");
const chartBlock = document.getElementById("chart-block");
const chartDay = document.getElementById("chart-day");
const chartWeek = document.getElementById("chart-week");
const chartMonth = document.getElementById("chart-month");
const dayChartBreakDownBlock = document.getElementById("day-chart-block");
// Show the corresponding chart when user clicks one of the three buttons
chartChoiceControl?.addEventListener("calciteChipGroupSelect", (event) => {
// clear feature effect on the layer view and clicked bar chart
for (let chart of charts) {
changeBarColor(chart, previouslySelectedBarIndex, "#007AC2");
layerView.featureEffect = undefined;
previouslySelectedBarIndex = null;
dayChartBreakDownBlock.style.display = "none";
chartDay.style.display = "none";
chartWeek.style.display = "none";
chartMonth.style.display = "none";
switch (event.target.selectedItems[0].value) {
chartBlock.heading = "Total accidents by time of day";
chartDay.style.display = "block";
chartBlock.heading = "Total accidents by day of week";
chartWeek.style.display = "block";
chartBlock.heading = "Total accidents by month";
chartMonth.style.display = "block";
// Initializes charts for accidents by hour, weekday, and month when the app loads.
function updateChart(canvasId, data, labels, remove = false, max = 3000) {
const canvasElement = document.getElementById(canvasId);
console.warn(`Canvas element with id "${canvasId}" not found.`);
const backgroundColors = Array(data.length).fill("#007AC2");
const chart = new Chart(canvasElement.getContext("2d"), {
datasets: [{ backgroundColor: backgroundColors, data }],
legend: { display: false },
`${context.label} - Total accidents: ${context.dataset.data[context.dataIndex]}`,
scales: { y: { beginAtZero: true, max } },
canvasElement.onclick = async function (event) {
await applyFilterToAccidentsData(event, chart);