<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>SceneLayerView - query statistics by geometry | Sample | ArcGIS Maps SDK for JavaScript</title>
<!-- Load the Chart.js library -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/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>
<arcgis-scene item-id="fb5948b2bb76439792786000b942e5b7">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle>
<arcgis-compass slot="top-left"></arcgis-compass>
<calcite-panel id="queryPanel" slot="bottom-left" heading="Query by geometry">
<div id="queryPanelContent">
<calcite-label>Draw a geometry to query by:</calcite-label>
<calcite-action-bar id="actionBar" layout="horizontal" expand-disabled>
icon="pin"></calcite-action>
icon="line"></calcite-action>
icon="polygon"></calcite-action>
<calcite-label for="bufferSlider">Set a geometry buffer size:</calcite-label>
label-handles></calcite-slider>
<calcite-button id="clearGeometry" width="full">Clear</calcite-button>
<calcite-panel id="resultPanel" slot="top-right">
<div id="resultPanelContent">
<span>Selected Buildings: </span>
<span id="count">0</span>
<canvas id="year-chart" width="250" height="200"> </canvas>
<canvas id="material-chart" width="250" height="300"> </canvas>
] = await $arcgis.import([
"@arcgis/core/layers/GraphicsLayer.js",
"@arcgis/core/widgets/Sketch/SketchViewModel.js",
"@arcgis/core/geometry/operators/geodesicBufferOperator.js",
"@arcgis/core/Graphic.js",
"@arcgis/core/core/promiseUtils.js",
"@arcgis/core/core/reactiveUtils.js",
/*****************************************************************
* Get a reference to the HTML elements
*****************************************************************/
const viewElement = document.querySelector("arcgis-scene");
const queryPanel = document.getElementById("queryPanel");
const resultPanel = document.getElementById("resultPanel");
const bufferSlider = document.getElementById("bufferSlider");
point: document.getElementById("pointBtn"),
polyline: document.getElementById("lineBtn"),
polygon: document.getElementById("polygonBtn"),
const clearGeometryBtn = document.getElementById("clearGeometry");
const countText = document.getElementById("count");
// Wait for the view to initialize
await viewElement.viewOnReady();
// Add a GraphicsLayer for the sketches and the buffer
const sketchLayer = new GraphicsLayer();
const bufferLayer = new GraphicsLayer();
viewElement.map.addMany([bufferLayer, sketchLayer]);
const sceneLayer = viewElement.map.layers.find((layer) => {
return layer.title === "Helsinki textured buildings";
sceneLayer.outFields = ["buildingMaterial", "yearCompleted"];
const sceneLayerView = await viewElement.whenLayerView(sceneLayer);
// Load the operator's dependencies
await geodesicBufferOperator.load();
// Use the SketchViewModel to draw polygons that are used as a query
let sketchGeometry = null;
const sketchViewModel = new SketchViewModel({
defaultUpdateOptions: { tool: "reshape", toggleToolOnClick: false },
defaultCreateOptions: { hasZ: false },
sketchViewModel.on("create", (event) => {
if (event.state === "complete") {
sketchGeometry = event.graphic.geometry;
sketchViewModel.on("update", (event) => {
if (event.state === "complete") {
sketchGeometry = event.graphics[0].geometry;
() => sketchViewModel.state,
const activeTool = sketchViewModel.createGraphic?.geometry?.type;
for (const tool in drawButtons) {
drawButtons[tool].active = activeTool === tool && state === "active";
/*****************************************************************
*****************************************************************/
bufferSlider.labelFormatter = function (value, type) {
// Get user entered values for buffer
bufferSlider.addEventListener("calciteSliderInput", (event) => {
bufferSize = event.target.value;
// Draw geometry buttons - use the selected geometry to sktech
Object.values(drawButtons).forEach((button) => {
button.addEventListener("click", () => {
sketchViewModel.create(button.getAttribute("value"));
// Clear the geometry and set the default renderer
clearGeometryBtn.onclick = () => {
// Show the UI when all is initialized
queryPanel.style.display = "block";
// *****************************************************************
// Clear the geometry and set the default renderer
function clearGeometry() {
sketchViewModel.cancel();
resultPanel.style.display = "none";
// Set the geometry query on the visible SceneLayerView
const debouncedRunQuery = promiseUtils.debounce(() => {
resultPanel.style.display = "block";
updateBufferGraphic(bufferSize);
return promiseUtils.eachAlways([queryStatistics(), updateSceneLayer()]);
debouncedRunQuery().catch((error) => {
if (error.name === "AbortError") {
// Set the renderer with objectIds
let highlightHandle = null;
function clearHighlighting() {
highlightHandle.remove();
function highlightBuildings(objectIds) {
// Remove any previous highlighting
countText.textContent = objectIds.length;
highlightHandle = sceneLayerView.highlight(objectIds);
// Update the graphic with buffer
function updateBufferGraphic(buffer) {
// Add a polygon graphic for the buffer
const bufferGeometry = geodesicBufferOperator.execute(sketchGeometry, buffer, {
if (bufferLayer.graphics.length === 0) {
geometry: bufferGeometry,
symbol: sketchViewModel.polygonSymbol,
bufferLayer.graphics.getItemAt(0).geometry = bufferGeometry;
function updateSceneLayer() {
const query = sceneLayerView.createQuery();
query.geometry = sketchGeometry;
query.distance = bufferSize;
return sceneLayerView.queryObjectIds(query).then(highlightBuildings);
let materialChart = null;
function queryStatistics() {
const statDefinitions = [
"CASE WHEN buildingMaterial = 'concrete or lightweight concrete' THEN 1 ELSE 0 END",
outStatisticFieldName: "material_concrete",
onStatisticField: "CASE WHEN buildingMaterial = 'brick' THEN 1 ELSE 0 END",
outStatisticFieldName: "material_brick",
onStatisticField: "CASE WHEN buildingMaterial = 'wood' THEN 1 ELSE 0 END",
outStatisticFieldName: "material_wood",
onStatisticField: "CASE WHEN buildingMaterial = 'steel' THEN 1 ELSE 0 END",
outStatisticFieldName: "material_steel",
"CASE WHEN buildingMaterial IN ('concrete or lightweight concrete', 'brick', 'wood', 'steel') THEN 0 ELSE 1 END",
outStatisticFieldName: "material_other",
"CASE WHEN (yearCompleted >= '1850' AND yearCompleted <= '1899') THEN 1 ELSE 0 END",
outStatisticFieldName: "year_1850",
"CASE WHEN (yearCompleted >= '1900' AND yearCompleted <= '1924') THEN 1 ELSE 0 END",
outStatisticFieldName: "year_1900",
"CASE WHEN (yearCompleted >= '1925' AND yearCompleted <= '1949') THEN 1 ELSE 0 END",
outStatisticFieldName: "year_1925",
"CASE WHEN (yearCompleted >= '1950' AND yearCompleted <= '1974') THEN 1 ELSE 0 END",
outStatisticFieldName: "year_1950",
"CASE WHEN (yearCompleted >= '1975' AND yearCompleted <= '1999') THEN 1 ELSE 0 END",
outStatisticFieldName: "year_1975",
"CASE WHEN (yearCompleted >= '2000' AND yearCompleted <= '2015') THEN 1 ELSE 0 END",
outStatisticFieldName: "year_2000",
const query = sceneLayerView.createQuery();
query.geometry = sketchGeometry;
query.distance = bufferSize;
query.outStatistics = statDefinitions;
return sceneLayerView.queryFeatures(query).then((result) => {
const allStats = result.features[0].attributes;
updateChart(materialChart, [
allStats.material_concrete,
// Updates the given chart with new data
function updateChart(chart, dataValues) {
chart.data.datasets[0].data = dataValues;
function createYearChart() {
const yearCanvas = document.getElementById("year-chart");
yearChart = new Chart(yearCanvas.getContext("2d"), {
labels: ["1850-1899", "1900-1924", "1925-1949", "1950-1974", "1975-1999", "2000-2015"],
backgroundColor: "#149dcf",
data: [0, 0, 0, 0, 0, 0],
maintainAspectRatio: false,
legend: { display: false },
title: { display: true, text: "Build Year" },
x: { stacked: true, ticks: { precision: 0, beginAtZero: true } },
function createMaterialChart() {
const materialCanvas = document.getElementById("material-chart");
materialChart = new Chart(materialCanvas.getContext("2d"), {
labels: ["Concrete", "Brick", "Wood", "Steel", "Other"],
backgroundColor: ["#FD7F6F", "#7EB0D5", "#B2E061", "#BD7EBE", "#FFB55A"],
maintainAspectRatio: false,
legend: { position: "bottom" },
title: { display: true, text: "Building Material" },
updateChart(materialChart, [0, 0, 0, 0, 0]);
updateChart(yearChart, [0, 0, 0, 0, 0, 0]);
countText.textContent = 0;