<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Elevation Profile analysis | 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>
grid-template-rows: 1fr auto;
padding: var(--calcite-spacing-lg) var(--calcite-spacing-xl);
padding: var(--calcite-spacing-md);
gap: var(--calcite-spacing-sm);
font-size: var(--calcite-font-size-sm);
<arcgis-scene item-id="2e95aee2a0d04e1a9ef72acb9f0a9c65">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle>
<arcgis-compass slot="top-left"></arcgis-compass>
<div slot="top-right" class="menu">
<calcite-button class="pick-button" width="full"
>Pick a cable railway route</calcite-button
<div class="help-message">
Click on a cable railway route in the scene to see its profile.
<calcite-button class="cancel-button" width="full" appearance="outline-fill"
>Cancel picking</calcite-button
<calcite-scrim></calcite-scrim>
<canvas class="chart"></canvas>
const [ElevationProfileAnalysis, Query, reactiveUtils, promiseUtils] = await $arcgis.import([
"@arcgis/core/analysis/ElevationProfileAnalysis.js",
"@arcgis/core/rest/support/Query.js",
"@arcgis/core/core/reactiveUtils.js",
"@arcgis/core/core/promiseUtils.js",
const initiallyPickedFeatureID = 184;
color: "rgb(160, 30, 220)",
legendBackgroundColor: "rgba(160, 30, 220, 0.2)",
color: "rgb(30, 160, 220)",
legendBackgroundColor: "rgba(255, 255, 255,0)",
const viewElement = document.querySelector("arcgis-scene");
const pickButton = document.querySelector(".pick-button");
const cancelButton = document.querySelector(".cancel-button");
const chartElement = document.querySelector(".chart");
const helpMessage = document.querySelector(".help-message");
const chartScrim = document.querySelector(".results > calcite-scrim");
// Controller for aborting an ongoing operation.
let abortController = null;
// Handle for removing any highlighted feature.
let highlightHandle = null;
// Wait for the view to be ready.
await viewElement.viewOnReady();
const cableRailwayLayer = viewElement.map.allLayers.find(
(layer) => layer.title === "CableCars",
/**********************************************
*********************************************/
const analysis = new ElevationProfileAnalysis({
// The first profile line samples the ground elevation
color: settings.ground.color,
// The second profile samples the clicked cabel railway line
color: settings.input.color,
viewElement.analyses.add(analysis);
const analysisView = await viewElement.whenAnalysisView(analysis);
/**********************************************
* Query a feature as the initially picked cable railway route and highlight it.
*********************************************/
const query = new Query({
// OBJECTID field is necessary to highlight the feature.
objectIds: [initiallyPickedFeatureID],
cableRailwayLayer.queryFeatures(query).then(async ({ features }) => {
analysis.geometry = features[0].geometry;
analysis.elevationInfo = cableRailwayLayer.elevationInfo;
await highlightFeature(features[0]);
/**********************************************
* Create the chart to display the analysis results.
* This sample uses the Chart.js library: https://www.chartjs.org/
*********************************************/
const ctx = chartElement.getContext("2d");
const chart = new Chart(ctx, {
maintainAspectRatio: false,
// Add handle to take care of the chart updates.
viewElement.view.addHandles([
// Update the chart UI as the analysis computation progresses.
() => analysisView.progress,
// Show a spinner while the analysis is being computed.
chartScrim.loading = progress > 0 && progress < 1;
// Update the chart on each step.
updateChartData(chart, analysisView);
// Picking button allows the user to choose a cable railway along which a new elevation profile will be computed.
pickButton.addEventListener("click", startPicking);
// "Cancel" button stops the picking process and updates the UI accordingly.
cancelButton.addEventListener("click", stopPicking);
// Start the picking on app load
/**********************************************
*********************************************/
async function startPicking() {
// Stop any pending picking operation.
// Create a new abort controller for the new operation and get a reference to its signal.
abortController = new AbortController();
const { signal } = abortController;
// Update the UI to reflect that the picking process has started.
// After one line is picked, call the pickFeature() method again.
// This is done until the picking is canceled/aborted.
while (!signal.aborted) {
// Start picking a feature from the scene and get reference to the returned results.
const pickedResult = await analysisView.pickFeature({
// Highlight the feature which was picked by the user.
await highlightFeature(pickedResult.feature, signal);
// Ignore abort errors. Other errors are unexpected and can be thrown.
if (!promiseUtils.isAbortError(error)) {
// If the abort controller is still the current one, clear it.
if (abortController?.signal === signal) {
async function highlightFeature(feature, signal) {
// Remove any existing highlight.
highlightHandle?.remove();
// Hide the dashed line which represents the analysis geometry since the feature is highlighted instead.
analysis.viewOptions.geometryVisualizationVisible = false;
// Access the layer view of the picked cable railway and highlight the feature.
const layerView = await viewElement.whenLayerView(feature.layer);
const handle = layerView.highlight(feature);
// Create a handle to be able to remove the highlight later.
analysis.viewOptions.geometryVisualizationVisible = true;
// Stop any pending picking operation with the abortController.
abortController?.abort();
// When an abort controller is present, the user is currently picking a feature.
pickButton.hidden = !!abortController;
cancelButton.hidden = !abortController;
helpMessage.hidden = !abortController;
function updateChartData(chart, { results }) {
// If analysis view result has no samples, clear the chart.
if (!results[0].samples?.length) {
// Update the chart datasets with the samples of each profile.
const datasets = results.map(({ samples, profile }, i) => ({
data: samples.map((point) => ({
borderColor: profile.color.toCss(false),
fill: profile.type == "ground", // Only use a fill for the ground
backgroundColor: settings[profile.type].legendBackgroundColor,
label: settings[profile.type].label,
chart.options.scales.x.max = results[0].statistics?.maxDistance;