<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Filter SceneLayer with FeatureFilter | 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>
border: 0.5px solid #cfcfcf;
calcite-segmented-control {
--calcite-segmented-control-border-color: #cfcfcf;
<arcgis-scene item-id="47241277f5c249d6b1c13840192a7cb0">
<arcgis-home slot="top-left"></arcgis-home>
<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="bottom-right" id="filterCard">
<div slot="heading">Filter by Geometry</div>
<div id="layerSelection" class="card-content">
<calcite-label>Select the layers to filter on:</calcite-label>
value="sceneLayerViewFilter"
label-text="Buildings"></calcite-checkbox>
value="featureLayerViewFilter"
label-text="Trees"></calcite-checkbox>
<div id="sketchDiv" class="card-content">
<calcite-label>Draw a geometry to filter by:</calcite-label>
hide-create-tools-multipoint
hide-selection-count-label
hide-selection-tools-lasso-selection
hide-selection-tools-rectangle-selection
default-graphics-layer-disabled>
<div id="spatialRelationshipDiv" class="card-content">
<calcite-label>Spatial relationship:</calcite-label>
<calcite-segmented-control>
<calcite-segmented-control-item value="disjoint" checked
>disjoint</calcite-segmented-control-item
<calcite-segmented-control-item value="intersects"
>intersects</calcite-segmented-control-item
<calcite-segmented-control-item value="contains"
>contains</calcite-segmented-control-item
</calcite-segmented-control>
<div id="bufferDiv" class="card-content">
<calcite-label>Set a geometry buffer size:</calcite-label>
<div id="clearDiv" class="card-content">
<calcite-button id="clearFilter" width="full">Clear Filter</calcite-button>
const [GraphicsLayer, FeatureFilter, geodesicBufferOperator, Graphic] = await $arcgis.import([
"@arcgis/core/layers/GraphicsLayer.js",
"@arcgis/core/layers/support/FeatureFilter.js",
"@arcgis/core/geometry/operators/geodesicBufferOperator.js",
"@arcgis/core/Graphic.js",
// Get references to the Scene and the Sketch component
const viewElement = document.querySelector("arcgis-scene");
const sketch = viewElement.querySelector("arcgis-sketch");
// Define symbol definitions for the sketch geometries
color: [255, 255, 255, 0.8],
color: [211, 132, 80, 0.7],
color: [211, 132, 80, 0.7],
color: [255, 255, 255, 0.8],
color: [211, 132, 80, 0.7],
let sceneLayerView = null;
let featureLayerView = null;
const sketchLayer = new GraphicsLayer({
elevationInfo: { mode: "on-the-ground" },
const bufferLayer = new GraphicsLayer({
elevationInfo: { mode: "on-the-ground" },
// Wait for the view and sketch component to be ready,
// add graphics layers for sketch and buffer to the map,
// load the geodesicBufferOperator module
await viewElement.viewOnReady();
await sketch.componentOnReady();
await geodesicBufferOperator.load();
viewElement.map.addMany([bufferLayer, sketchLayer]);
document.getElementById("filterCard").style.display = "block";
// Connect Layer checkboxes to filter logic
const featureLayerCheckbox = document.querySelector(
'calcite-checkbox[value="featureLayerViewFilter"]',
const sceneLayerCheckbox = document.querySelector(
'calcite-checkbox[value="sceneLayerViewFilter"]',
let featureLayerViewFilterSelected = featureLayerCheckbox.checked;
let sceneLayerViewFilterSelected = sceneLayerCheckbox.checked;
// Add event listeners to the layer checkboxes
featureLayerCheckbox.addEventListener("calciteCheckboxChange", (event) => {
featureLayerViewFilterSelected = event.target.checked;
sceneLayerCheckbox.addEventListener("calciteCheckboxChange", (event) => {
sceneLayerViewFilterSelected = event.target.checked;
let sketchGeometry = null;
sketch.layer = sketchLayer;
sketch.pointSymbol = sketchSymbols.point;
sketch.polylineSymbol = sketchSymbols.polyline;
sketch.polygonSymbol = sketchSymbols.polygon;
// For all feature and scene layers create a LayerView to add the filter to
for (const layer of viewElement.map.layers) {
const layerView = await viewElement.view.whenLayerView(layer);
if (layer.type === "feature") featureLayerView = layerView;
if (layer.type === "scene") sceneLayerView = layerView;
console.error("Error loading layer view:", err);
// Listen for geometry creation
sketch.addEventListener("arcgisCreate", (event) => {
if (event.detail.state === "start") {
// Clear previous filter when starting a new sketch
// Only once the geometry is completed
if (event.detail.state === "complete") {
// event.detail.graphic.geometry contains the drawn geometry
sketchGeometry = event.detail.graphic.geometry;
// Listen for geometry update
sketch.addEventListener("arcgisUpdate", (event) => {
// event.detail.graphics is an array of updated graphics
if (event.detail.graphics && event.detail.graphics.length > 0) {
sketchGeometry = event.detail.graphics[0].geometry;
// Listen for geometry deletion
sketch.addEventListener("arcgisDelete", (event) => {
// Clear filter when sketch geometry is deleted
// get the selected spatialRelationship from Calcite segmented control
const spatialRelationshipControl = document.querySelector("calcite-segmented-control");
let selectedFilter = spatialRelationshipControl.querySelector("[checked]").value;
spatialRelationshipControl.addEventListener("calciteSegmentedControlChange", (event) => {
selectedFilter = event.target.value;
// Wire up new Calcite slider for buffer size
const bufferSlider = document.querySelector("calcite-slider#bufferSlider");
let bufferSize = bufferSlider.value;
bufferSlider.addEventListener("calciteSliderInput", (event) => {
bufferSize = event.target.value;
// Handle Clear filter button click
const clearFilterButton = document.querySelector("calcite-button#clearFilter");
clearFilterButton.addEventListener("click", clearFilter);
// Removes all the filters and graphics
sceneLayerView.filter = null;
featureLayerView.filter = null;
// Set the geometry filter on the visible LayerViews
function updateFilter() {
// autocasts to FeatureFilter
geometry: filterGeometry,
spatialRelationship: selectedFilter,
if (featureLayerViewFilterSelected) {
featureLayerView.filter = featureFilter;
featureLayerView.filter = null;
if (sceneLayerViewFilterSelected) {
sceneLayerView.filter = featureFilter;
sceneLayerView.filter = null;
// Update the filter geometry depending on the buffer
// If a buffer is set, use the buffered geometry as filter geometry
// otherwise use the sketch geometry.
let filterGeometry = null;
function updateFilterGeometry() {
// add a polygon graphic for the bufferSize
const bufferGeometry = geodesicBufferOperator.execute(sketchGeometry, bufferSize, {
if (bufferLayer.graphics.length === 0) {
geometry: bufferGeometry,
symbol: sketch.polygonSymbol,
bufferLayer.graphics.getItemAt(0).geometry = bufferGeometry;
filterGeometry = bufferGeometry;
filterGeometry = sketchGeometry;