<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Filter SceneLayer with SceneFilter | Sample | ArcGIS Maps SDK for JavaScript</title>
<!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
<script type="module" src="https://js.arcgis.com/5.0/"></script>
justify-content: space-evenly;
justify-content: space-around;
<arcgis-scene item-id="fa4dd90d0bfd470fb0dc9e32eab0bfef">
<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 slot="top-right" id="sketch">
>Use the sketch component to create and modify the filter polygons:</span
hide-create-tools-polyline
default-graphics-layer-disabled>
<calcite-button id="contains" appearance="solid">Contains</calcite-button>
<calcite-button id="disjoint" appearance="outline">Disjoint</calcite-button>
<calcite-card slot="top-right">
<span slot="heading">Save scene as:</span>
<calcite-input id="webSceneTitle" placeholder="Scene Filter Sample"></calcite-input>
<calcite-button id="saveWebScene">Save</calcite-button>
<calcite-button id="ok">OK</calcite-button>
const [GraphicsLayer, Graphic, SceneFilter] = await $arcgis.import([
"@arcgis/core/layers/GraphicsLayer.js",
"@arcgis/core/Graphic.js",
"@arcgis/core/layers/support/SceneFilter.js",
// Get a reference to the Scene element
const viewElement = document.querySelector("arcgis-scene");
/***********************************
***********************************/
// Define symbol for the filter polygons
type: "simple-fill", // autocasts as new SimpleFillSymbol()
color: [255, 140, 0, 0.3],
// autocasts as new SimpleLineSymbol()
// Prepare one starting filter polygon
const graphic = new Graphic({
// Make a new layer for the masking filters
const filterLayer = new GraphicsLayer({
/***********************************
* Sketch component functionality
***********************************/
// Variable to keep track of which spatial relationship is selected
let spatialRelationship = "contains";
const sketch = document.querySelector("arcgis-sketch");
sketch.layer = filterLayer;
sketch.polygonSymbol = filterSymbol;
// Listen to sketch component's create event to update the filter
sketch.addEventListener("arcgisCreate", (event) => {
// Only once the polygon is completed
if (event.detail.state === "complete") {
// Listen to sketch component's update event to update the filter
sketch.addEventListener("arcgisUpdate", (event) => {
// Only if the update event is of type "stop", meaning only once the user stopped dragging
// There are different stop events, like 'move-stop', 'rotate-stop', 'scale-stop', etc.
if (event.detail.toolEventInfo && event.detail.toolEventInfo.type.includes("stop")) {
// Listen to sketch component's delete event to update the filter
sketch.addEventListener("arcgisDelete", () => {
// Update the filter to reflect the modified graphics layer.
function updateFilter() {
// Extract the geometries from the graphics layer
for (let i in filterLayer.graphics.items) {
geometries.push(filterLayer.graphics.items[i].geometry);
// Set the filter on the buildings layer
osmBuildings.filter = new SceneFilter({
spatialRelationship: spatialRelationship,
// Set the filter on the tree layer
osmTrees.filter = new SceneFilter({
spatialRelationship: spatialRelationship,
/***********************************
***********************************/
// Wait for the view to initialize
await viewElement.viewOnReady();
// Set the camera to focus on Warsaw, Poland
// Add filter layer to the map
viewElement.map.add(filterLayer);
// Read the layers from the webscene
osmTrees = viewElement.map.allLayers.find((layer) => {
return layer.title == "Realistic"; // There are two tree layers, we want the realistic trees
osmBuildings = viewElement.map.allLayers.find((layer) => {
return layer.title === "OpenStreetMap 3D Buildings";
// Apply the SceneFilter to both layers
/***********************************
* Button functionality to set the spatial relationship of the layer
***********************************/
const contains = document.getElementById("contains");
const disjoint = document.getElementById("disjoint");
contains.addEventListener("click", (event) => {
spatialRelationship = "contains";
// Styling of the buttons
disjoint.appearance = "outline";
contains.appearance = "solid";
disjoint.addEventListener("click", (event) => {
spatialRelationship = "disjoint";
// Styling of the buttons
disjoint.appearance = "solid";
contains.appearance = "outline";
/***********************************
* Button functionality to save the webscene
***********************************/
const title = document.getElementById("webSceneTitle");
const save = document.getElementById("saveWebScene");
const overlay = document.getElementById("overlayDiv");
const info = document.getElementById("info");
const ok = document.getElementById("ok");
// Start the process of saving the scene as a new webscene
save.addEventListener("click", () => {
ok.style.display = "none";
info.textContent = "Saving...";
overlay.style.visibility = "visible";
// Disable the button while saving
// Get a reference to the WebScene
const scene = viewElement.map;
// Item automatically casts to a PortalItem instance by saveAs
scene.remove(filterLayer); // We need to remove filter layer since GraphicLayers cannot be persisted
// Update properties of the WebScene related to the view.
scene.updateFrom(viewElement.view).then(() => {
// link to the newly-created web scene item
const itemPageUrl = item.portal.url + "/home/item.html?id=" + item.id;
const link = '<a target="_blank" href="' + itemPageUrl + '">' + title.value + "</a>";
statusMessage("Successfully saved as <i>" + link + "</i>");
// Save didn't work correctly
statusMessage("Error " + error);
function statusMessage(message) {
viewElement.map.add(filterLayer); // Add the filter layer back to continue working
info.innerHTML = message;
ok.style.display = "block";
ok.addEventListener("click", () => {
overlay.style.visibility = "hidden";