Learn how to create and manage a client-side viewshed analysis in a 3D scene.
This tutorial uses a viewshed analysis to walk through a typical analysis lifecycle: create the analysis object, add it to the scene, obtain its analysis view, place or edit it interactively, update its properties, and clear it when you need to reset the workflow.
Place additional viewsheds in the scene, cancel placement, limit the field of view, and clear the analysis.
Prerequisites
Steps
Create a new pen
- To get started, either complete the Display a scene tutorial or
.
Get an access token
You need an access token
Go to the Create an API key tutorial and create an API key
An API key is a long-lived access token created using API key credentials. They are valid for up to one year and are typically embedded directly into client applications. with the following privilege(s)Privileges are a set of permissions assigned to ArcGIS accounts, developer credentials, and applications that grant access to secure resources and functionality in ArcGIS. :- Privileges
Location services > Basemaps
In CodePen, set the
apiKeyproperty on the globalesriConfigvariable to your access token.var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};
To learn about other ways to get an access token, go to Types of authentication.
Load the web scene
Use the portal item IDarcgis-scene component to load a web scene that provides the 3D context for the analysis.
-
Replace the
arcgis-scenefrom the Display a Scene starter with one that loads the web scene and includes zoom, navigation toggle, and compass components.3 collapsed lines<html><body><arcgis-scenebasemap="arcgis/topographic"ground="world-elevation"camera-position="-118.808, 33.961, 2000"camera-tilt="75"></arcgis-scene><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"></arcgis-scene>3 collapsed lines</body></html>
Add analysis controls
Add a small UI panel for placing, canceling, and clearing viewsheds.
-
Add CSS for the control panel and instructional text.
15 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style>142 collapsed lines</head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html> -
Add a
calcite-cardwith buttons and a switch to control the analysis workflow.50 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card>5 collapsed lines</arcgis-scene></body></html>
Add the analysis modules
Use $arcgis.import to load the modules required for the analysis object, its item type, and the placement workflow.
-
In a new
<script>at the bottom of the<body>, import the modules.The ArcGIS Maps SDK for JavaScript
ArcGIS Maps SDK for JavaScript, previously known as ArcGIS API for JavaScript, is a developer product for building mapping and spatial analysis applications for the web. is available via CDN and npm, but this tutorial is based on CDN. The$arcgis.importglobal function accepts a module path or array of module paths, and returns a promise that resolves with the requested modules. This function can only be used when working with the CDN; otherwise, use the standard import syntax. To learn more about the SDK’s different modules, visit the References page.65 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);</script>3 collapsed lines</body></html>
Create the analysis object
Create a ViewshedAnalysis and add an initial Viewshed so the scene starts with a concrete analysis object already in place.
-
Get references to the scene and UI elements, then wait for the scene to be ready.
75 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();99 collapsed linesconst viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html> -
Create a
ViewshedAnalysis, add a programmaticViewshed, and then add the analysis to the scene’sanalysescollection.84 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);81 collapsed linesconst analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html>
Get the analysis view
The analysis object stores the state of the analysis. The analysis view exposes interactive, view-specific behavior such as selection and placement.
-
Call
whenAnalysisView()to obtain the ViewshedAnalysisView3D. -
Set
interactivetotrueso the selected viewshed can be edited in the scene. -
Set
selectedViewshedto the programmatic viewshed so it is active immediately.102 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;77 collapsed lineslet abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html>
Place additional viewsheds interactively
To create a custom analysis workflow, call place() on the analysis view when the user clicks the button in your application UI.
-
Add event listeners and create an
AbortControllervariable to track the active placement operation.106 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);73 collapsed linesfunction updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html> -
Add a small helper function to keep the UI in sync while placement starts and stops.
110 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();63 collapsed linesasync function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html> -
Create a
startPlacing()function that starts placement by callinganalysisView.place().120 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}41 collapsed linescancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html> -
Run the app and click Place viewshed. Click once in the scene to set the observer point, then click again to set the target.
Cancel placement with AbortController
Interactive placement should be cancellable from your own UI.
-
Add a
stopPlacing()function that callsabort()on the current controller.142 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}33 collapsed lineslimitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html> -
Click Place viewshed, then click Cancel before finishing the placement to stop the operation.
Update the selected viewshed
After a viewshed exists in the analysis, you can modify its properties directly.
-
Add a function that limits the selected viewshed’s horizontal and vertical field of view.
150 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}13 collapsed linesresetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}</script></body></html> -
Run the app and use the switch while editing a viewshed to keep its field of view within the defined maximum values.
-
When a viewshed is selected in the scene, press
Deleteon the keyboard to remove only that viewshed.
Clear the analysis
To restart the workflow, clear the analysis and remove its current viewsheds.
-
Add a
resetAnalysis()function that stops any active placement and clears the analysis.170 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Create and manage a viewshed analysis</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.1/"></script><style>html,body {height: 100%;margin: 0;}#viewshedControls {width: 280px;}#viewshedControls calcite-button {display: flex;}#promptText {display: none;margin-top: 0.5rem;}#limitFOV {margin-top: 0.5rem;padding-top: 0.5rem;}</style></head><body><arcgis-scene id="viewElement" item-id="f212abf31a424b77a5a8dde92302423b"><arcgis-zoom slot="top-left"></arcgis-zoom><arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle><arcgis-compass slot="top-left"></arcgis-compass><calcite-card id="viewshedControls" slot="top-right"><calcite-button id="placeButton">Place viewshed</calcite-button><calcite-button id="cancelButton" style="display: none">Cancel</calcite-button><calcite-button id="resetButton" appearance="outline">Clear viewsheds</calcite-button><div id="promptText"><em>Click in the scene to place an observer point and then set the target.</em></div><calcite-label layout="inline-space-between" id="limitFOV">Limit maximum field of view<calcite-switch checked id="limitFOVSwitch"></calcite-switch></calcite-label></calcite-card></arcgis-scene><script type="module">const [promiseUtils, reactiveUtils, SpatialReference, Viewshed, ViewshedAnalysis] = await $arcgis.import(["@arcgis/core/core/promiseUtils.js","@arcgis/core/core/reactiveUtils.js","@arcgis/core/geometry/SpatialReference.js","@arcgis/core/analysis/Viewshed.js","@arcgis/core/analysis/ViewshedAnalysis.js",]);const viewElement = document.getElementById("viewElement");const placeButton = document.getElementById("placeButton");const cancelButton = document.getElementById("cancelButton");const resetButton = document.getElementById("resetButton");const promptText = document.getElementById("promptText");const limitFOVSwitch = document.getElementById("limitFOVSwitch");await viewElement.viewOnReady();const viewshedAnalysis = new ViewshedAnalysis();const programmaticViewshed = new Viewshed({observer: {spatialReference: SpatialReference.WebMercator,x: -9754426,y: 5143111,z: 330,},farDistance: 900,tilt: 84,heading: 63,horizontalFieldOfView: 85,verticalFieldOfView: 60,});viewshedAnalysis.viewsheds.add(programmaticViewshed);viewElement.analyses.add(viewshedAnalysis);const analysisView = await viewElement.whenAnalysisView(viewshedAnalysis);analysisView.interactive = true;analysisView.selectedViewshed = programmaticViewshed;let abortController = null;placeButton.addEventListener("click", startPlacing);function updateUI() {const placing = abortController !== null;placeButton.style.display = placing ? "none" : "flex";cancelButton.style.display = placing ? "flex" : "none";promptText.style.display = placing ? "block" : "none";}updateUI();async function startPlacing() {abortController?.abort();abortController = new AbortController();const { signal } = abortController;updateUI();try {await analysisView.place({ signal });} catch (error) {if (!promiseUtils.isAbortError(error)) {throw error;}} finally {if (abortController?.signal === signal) {abortController = null;}updateUI();}}cancelButton.addEventListener("click", stopPlacing);function stopPlacing() {abortController?.abort();abortController = null;updateUI();}limitFOVSwitch.addEventListener("calciteSwitchChange", applyFieldOfViewLimit);reactiveUtils.watch(() => analysisView?.selectedViewshed,() => {applyFieldOfViewLimit();},);function applyFieldOfViewLimit() {const selectedViewshed = analysisView.selectedViewshed;if (!selectedViewshed || !limitFOVSwitch.checked) {return;}selectedViewshed.horizontalFieldOfView = Math.min(selectedViewshed.horizontalFieldOfView, 120);selectedViewshed.verticalFieldOfView = Math.min(selectedViewshed.verticalFieldOfView, 90);}resetButton.addEventListener("click", resetAnalysis);function resetAnalysis() {stopPlacing();viewshedAnalysis.clear();analysisView.selectedViewshed = null;}5 collapsed lines</script></body></html> -
Run the app and click Place viewshed. Click once in the scene to set the observer point, then click again to set the target. Click Cancel to stop placement, use the switch while editing a viewshed to limit the field of view, and click Clear viewsheds to reset the workflow.
Run the app
In CodePen, run your code to display the scene with one programmatic viewshed, then place more viewsheds, cancel placement, constrain the selected viewshed’s field of view, remove a selected viewshed with Delete on the keyboard, and clear the analysis to start again.
What’s next?
Learn how to use other analyses in the Introduction to analyses, the Analysis objects sample, and the full set of analysis samples.