Referring to the above sample we have made some modifications where we have essentially repeated the query for two image services with similar parameters. Unfortunatly it seems that only one of the services return the results based on the buffer and mouse event and draw a chart while the other service is left out.
Chart elemnts have been created for both layers and it would be great if you could let us know why this has been occuring.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<title>
ImageryLayer - client side chart | Sample | ArcGIS API for JavaScript 4.16
</title>
<link
rel="stylesheet"
/>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#titleDiv,
#infoDiv {
padding: 10px;
width: 320px;
}
#infoDiv {
height: 425px;
font-size: 14px;
display: none;
overflow-y: auto;
overflow-x: hidden;
}
#titleText {
font-size: 20pt;
font-weight: 30;
padding-bottom: 10px;
}
.chart-legend li span {
display: inline-block;
width: 12px;
height: 12px;
margin-right: 5px;
}
ul {
list-style-type: none;
}
</style>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/ImageryLayer",
"esri/widgets/LayerList",
"esri/Graphic",
"esri/geometry/Circle",
"esri/layers/support/TileInfo",
"esri/core/promiseUtils",
"esri/widgets/Expand"
], function (
Map,
MapView,
ImageryLayer,
LayerList,
Graphic,
Circle,
TileInfo,
promiseUtils,
Expand
) {
let landCoverChart, fragCoverChart, pixelValCount, pixelData;
let rasterAttributeFeatures = {};
const map = new Map();
const view = new MapView({
container: "viewDiv",
map: map,
center: [-80, 40.5],
zoom: 11,
constraints: {
rotationEnabled: false,
minScale: 36978595, // U.S.
// There is no basemap so lods are not set.
// So specify AGOL lods for the view
lods: TileInfo.create().lods
}
});
// initialize the imagery layer with lerc format
const imageryLayer = new ImageryLayer({
url:
format: "lerc"
});
const imageryLayer2 = new ImageryLayer({
url:
format: "lerc"
});
map.add(imageryLayer2);
map.add(imageryLayer);
// graphic that will represent one mile buffer as user drags the pointer
// over the map to view land cover types within one mile of the pointer
const graphic = new Graphic({
geometry: null,
symbol: {
type: "simple-fill",
color: null,
style: "solid",
outline: {
color: "blue",
width: 2
}
}
});
view.graphics.add(graphic);
// add the UI for titles, stats and chart.
view.ui.add("titleDiv", "top-right");
const infoDiv = document.getElementById("infoDiv");
view.ui.add(infoDiv, "top-right");
view.whenLayerView(imageryLayer).then(layerLoaded);
function layerLoaded(layerView) {
// watch for the imagery layer view's updating property
// to get the updated pixel values
layerView.watch("updating", function (value) {
if (!value) {
pixelData = layerView.pixelData;
}
});
// when the layer loads, listen to the view's drag and click
// events to update the land cover types chart to reflect an
// area within 1 mile of the pointer location.
removeChartEvents = view.on(["drag", "click"], function (event) {
if (pixelData) {
event.stopPropagation();
getFragCoverPixelInfo(event).then(updatefragCoverChart);
}
});
// raster attributes table returns categorical mapping of pixel values such as class and group
const attributesData =
imageryLayer.serviceRasterInfo.attributeTable.features;
// rasterAttributeFeatures will be used to add legend labels and colors for each
// land use type
for (var index in attributesData) {
if (attributesData) {
var hexColor = rgbToHex(
attributesData[index].attributes.Red,
attributesData[index].attributes.Green,
attributesData[index].attributes.Blue
);
rasterAttributeFeatures[
attributesData[index].attributes.Value
] = {
ClassName: attributesData[index].attributes.FragClass,
hexColor: hexColor
};
}
}
// initialize the land cover pie chart
createfragCoverChart();
}
// This function is called as user drags the pointer over or clicks on the view.
// Here we figure out which pixels fall within one mile of the
// pointer location and update the chart accordingly
const getFragCoverPixelInfo = promiseUtils.debounce(function (event) {
var currentExtent = pixelData.extent;
var pixelBlock = pixelData.pixelBlock;
const height = pixelBlock.height;
const width = pixelBlock.width;
// map point for the pointer location.
const point = view.toMap({
x: event.x,
y: event.y
});
// pointer x, y in pixels
const reqX = Math.ceil(event.x);
const reqY = Math.ceil(event.y);
// calculate how many meters are represented by 1 pixel.
const pixelSizeX =
Math.abs(currentExtent.xmax - currentExtent.xmin) / width;
// calculate how many pixels represent one mile
const bufferDim = Math.ceil(16093 / pixelSizeX);
// figure out 2 mile extent around the pointer location
const xmin = reqX - bufferDim < 0 ? 0 : reqX - bufferDim;
const ymin = reqY - bufferDim < 0 ? 0 : reqY - bufferDim;
const startPixel = ymin * width + xmin;
const bufferlength = bufferDim * 2;
const pixels = pixelBlock.pixels[0];
let oneMilePixelValues = [];
const radius2 = bufferDim * bufferDim;
// cover pixels within to 2 mile rectangle
if (bufferlength) {
for (var i = 0; i <= bufferlength; i++) {
for (var j = 0; j <= bufferlength; j++) {
// check if the given pixel location is in within one mile of the pointer
// add its value to pixelValue.
if (
Math.pow(i - bufferDim, 2) + Math.pow(j - bufferDim, 2) <=
radius2
) {
var pixelValue =
pixels[Math.floor(startPixel + i * width + j)];
}
if (pixelValue !== undefined) {
oneMilePixelValues.push(pixelValue);
}
}
}
} else {
oneMilePixelValues.push(pixels[startPixel]);
}
pixelValCount = {};
// get the count of each land type returned within one mile raduis
for (var i = 0; i < oneMilePixelValues.length; i++) {
pixelValCount[oneMilePixelValues[i]] =
1 + (pixelValCount[oneMilePixelValues[i]] || 0);
}
var circle = new Circle({
center: point,
radius: bufferDim * pixelSizeX
});
graphic.geometry = circle;
});
// This function is called once pixel values within one mile of the pointer
// location are processed and ready for the chart update.
function updatefragCoverChart() {
if (infoDiv.style.display != "block") {
infoDiv.style.display = "block";
}
fragCoverChart.data.datasets[0].data = [];
var dataset = [];
var landCoverTypeColors = [];
var landCoverTypeLabels = [];
// pixelValCount object contains land cover types and count of pixels
// that represent that type in within one mile.
for (var index in pixelValCount) {
if (index == 0) {
landCoverTypeColors.push("rgba(255,255,255,1");
landCoverTypeLabels.push("NoData");
} else {
var color = rasterAttributeFeatures[index].hexColor;
landCoverTypeColors.push(color);
landCoverTypeLabels.push(
rasterAttributeFeatures[index].ClassName
);
}
fragCoverChart.data.datasets[0].data.push(pixelValCount[index]);
}
fragCoverChart.data.datasets[0].backgroundColor = landCoverTypeColors;
fragCoverChart.data.labels = landCoverTypeLabels;
fragCoverChart.update(0);
document.getElementById(
"chartLegend"
).innerHTML = fragCoverChart.generateLegend();
}
// This function is called when the ImageryLayer is loaded.
// It sets up the land cover types chart.
function createfragCoverChart() {
const landCoverCanvas = document.getElementById("landcover-chart1");
fragCoverChart = new Chart(landCoverCanvas.getContext("2d"), {
type: "horizontalBar",
data: {
labels: [],
datasets: [
{
data: [],
backgroundColor: [],
borderColor: "rgb(0, 0, 0, 0, 1)",
borderWidth: 0.5
}
]
},
options: {
responsive: false,
cutoutPercentage: 35,
legend: {
display: false
},
title: {
display: true,
text: "Fragmentaton Types"
},
plugins: {
datalabels: {
formatter: function (value, ctx) {
let datasets = ctx.chart.data.datasets;
if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
let sum = datasets[0].data.reduce(function (a, b) {
return a + b;
});
let percentage = Math.round((value / sum) * 100);
if (percentage > 15) {
return percentage + "%";
} else {
return "";
}
} else {
return percentage;
}
},
color: "#4c4c4c"
}
}
}
});
}
view.whenLayerView(imageryLayer2).then(layerLoaded);
function layerLoaded(layerView) {
// watch for the imagery layer view's updating property
// to get the updated pixel values
layerView.watch("updating", function (value) {
if (!value) {
pixelData = layerView.pixelData;
}
});
// when the layer loads, listen to the view's drag and click
// events to update the land cover types chart to reflect an
// area within 1 mile of the pointer location.
removeChartEvents = view.on(["drag", "click"], function (event) {
if (pixelData) {
event.stopPropagation();
getLandCoverPixelInfo(event).then(updatelandCoverChart);
}
});
// raster attributes table returns categorical mapping of pixel values such as class and group
const attributesData =
imageryLayer2.serviceRasterInfo.attributeTable.features;
// rasterAttributeFeatures will be used to add legend labels and colors for each
// land use type
for (var index in attributesData) {
if (attributesData) {
var hexColor = rgbToHex(
attributesData[index].attributes.Red,
attributesData[index].attributes.Green,
attributesData[index].attributes.Blue
);
rasterAttributeFeatures[
attributesData[index].attributes.Value
] = {
ClassName: attributesData[index].attributes.ClassName,
hexColor: hexColor
};
}
}
// initialize the land cover pie chart
createlandCoverChart();
}
// This function is called as user drags the pointer over or clicks on the view.
// Here we figure out which pixels fall within one mile of the
// pointer location and update the chart accordingly
const getLandCoverPixelInfo = promiseUtils.debounce(function (event) {
var currentExtent = pixelData.extent;
var pixelBlock = pixelData.pixelBlock;
const height = pixelBlock.height;
const width = pixelBlock.width;
// map point for the pointer location.
const point = view.toMap({
x: event.x,
y: event.y
});
// pointer x, y in pixels
const reqX = Math.ceil(event.x);
const reqY = Math.ceil(event.y);
// calculate how many meters are represented by 1 pixel.
const pixelSizeX =
Math.abs(currentExtent.xmax - currentExtent.xmin) / width;
// calculate how many pixels represent one mile
const bufferDim = Math.ceil(16093 / pixelSizeX);
// figure out 2 mile extent around the pointer location
const xmin = reqX - bufferDim < 0 ? 0 : reqX - bufferDim;
const ymin = reqY - bufferDim < 0 ? 0 : reqY - bufferDim;
const startPixel = ymin * width + xmin;
const bufferlength = bufferDim * 2;
const pixels = pixelBlock.pixels[0];
let oneMilePixelValues = [];
const radius2 = bufferDim * bufferDim;
// cover pixels within to 2 mile rectangle
if (bufferlength) {
for (var i = 0; i <= bufferlength; i++) {
for (var j = 0; j <= bufferlength; j++) {
// check if the given pixel location is in within one mile of the pointer
// add its value to pixelValue.
if (
Math.pow(i - bufferDim, 2) + Math.pow(j - bufferDim, 2) <=
radius2
) {
var pixelValue =
pixels[Math.floor(startPixel + i * width + j)];
}
if (pixelValue !== undefined) {
oneMilePixelValues.push(pixelValue);
}
}
}
} else {
oneMilePixelValues.push(pixels[startPixel]);
}
pixelValCount = {};
// get the count of each land type returned within one mile raduis
for (var i = 0; i < oneMilePixelValues.length; i++) {
pixelValCount[oneMilePixelValues[i]] =
1 + (pixelValCount[oneMilePixelValues[i]] || 0);
}
var circle = new Circle({
center: point,
radius: bufferDim * pixelSizeX
});
graphic.geometry = circle;
});
function updatelandCoverChart() {
if (infoDiv.style.display != "block") {
infoDiv.style.display = "block";
}
landCoverChart.data.datasets[0].data = [];
var dataset = [];
var landCoverTypeColors = [];
var landCoverTypeLabels = [];
// pixelValCount object contains land cover types and count of pixels
// that represent that type in within one mile.
for (var index in pixelValCount) {
if (index == 0) {
landCoverTypeColors.push("rgba(255,255,255,1");
landCoverTypeLabels.push("NoData");
} else {
var color = rasterAttributeFeatures[index].hexColor;
landCoverTypeColors.push(color);
landCoverTypeLabels.push(
rasterAttributeFeatures[index].ClassName
);
}
landCoverChart.data.datasets[0].data.push(pixelValCount[index]);
}
landCoverChart.data.datasets[0].backgroundColor = landCoverTypeColors;
landCoverChart.data.labels = landCoverTypeLabels;
landCoverChart.update(0);
document.getElementById(
"chartLegend2"
).innerHTML = landCoverChart.generateLegend();
}
function createlandCoverChart() {
const landCoverCanvas = document.getElementById("landcover-chart2");
landCoverChart = new Chart(landCoverCanvas.getContext("2d"), {
type: "doughnut",
data: {
labels: [],
datasets: [
{
data: [],
backgroundColor: [],
borderColor: "rgb(0, 0, 0, 0, 1)",
borderWidth: 0.5
}
]
},
options: {
responsive: false,
cutoutPercentage: 35,
legend: {
display: false
},
title: {
display: true,
text: "Classification Types"
},
plugins: {
datalabels: {
formatter: function (value, ctx) {
let datasets = ctx.chart.data.datasets;
if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
let sum = datasets[0].data.reduce(function (a, b) {
return a + b;
});
let percentage = Math.round((value / sum) * 100);
if (percentage > 15) {
return percentage + "%";
} else {
return "";
}
} else {
return percentage;
}
},
color: "#4c4c4c"
}
}
}
});
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return (
"#" + componentToHex(r) + componentToHex(g) + componentToHex(b)
);
}
const instructionsExpand = new Expand({
expandIconClass: "esri-icon-question",
expandTooltip: "How to use this sample",
view: view,
expanded: true,
content:
"<div style='width:200px; padding:10px; background-color:white'><b>Drag</b> the pointer over the data or <b>click</b> to view the land cover types within 1 mile of the pointer location. <br><br><b>Click</b> the button below to toggle between view panning and the chart.</div>"
});
view.ui.add(instructionsExpand, "top-left");
// Close the 'help' popup when view is focused
view.watch("focused", function (isFocused) {
if (isFocused) {
instructionsExpand.expanded = false;
}
});
let chartEnabled = true;
let removeChartEvents;
const enableChartButton = document.getElementById("enableChart");
view.ui.add(enableChartButton, "top-left");
enableChartButton.addEventListener("click", function () {
chartEnabled = chartEnabled ? false : true;
if (chartEnabled) {
enableChartButton.classList.add("esri-icon-pie-chart");
enableChartButton.classList.remove("esri-icon-pan");
removeChartEvents = view.on(["drag", "click"], function (event) {
if (pixelData) {
event.stopPropagation();
getLandCoverPixelInfo(event).then(updatelandCoverChart, updatefragCoverChart);
}
});
} else {
removeChartEvents.remove();
removeChartEvents = null;
enableChartButton.classList.remove("esri-icon-pie-chart");
enableChartButton.classList.add("esri-icon-pan");
graphic.geometry = null;
}
});
const expand = new Expand({
view: view,
content: document.getElementById("infoDiv"),
expanded: true,
expandIconClass: "esri-icon-settings2"
})
view.ui.add(expand, "top-right");
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="titleDiv" class="esri-widget">
<div id="titleText">Testing Application</div>
<div>100 km radius</div>
</div>
<div id="infoDiv" class="esri-widget">
<canvas
id="landcover-chart2"
height="300"
width="300"
class="esri-widget"
></canvas>
<div id="chartLegend2" class="chart-legend esri-widget"></div>
<canvas
id="landcover-chart1"
height="300"
width="300"
class="esri-widget"
></canvas>
<div id="chartLegend" class="chart-legend esri-widget"></div>
</div>
<div
id="enableChart"
class="esri-widget--button esri-icon-pie-chart"
title="Toggle between panning and chart"
></div>
</body>
</html>