Filter 3D scene features out of a given geometry with a polygon filter.

Use case
You can directly control what users see within a specific scene view to give a more focused or cleaner user experience by using a SceneLayerPolygonFilter to selectively show or hide scene features within a given area.
How to use the sample
The sample initializes showing the “Navigation” 3D Basemap. Click the “Filter 3D buildings in extent” button, to set a SceneLayerPolygonFilter and filter out the Esri 3D buildings within the extent of a detailed buildings scene layer. Notice how the Esri 3D buildings within and intersecting the extent of the detailed buildings layer are hidden. Click the “Show detailed buildings” button to load a scene layer that contains more detailed buildings. Click the “Reset scene” button to hide the detailed buildings scene layer and clear the 3D buildings filter.
How it works
- Create a
Surfacefor the scene and set the World Elevation 3D as an elevation source. - Construct a
Basemapfor the scene using the “Navigation” 3D Basemap, load it, then search for the “buildings” baselayer. - Add the 3D San Francisco Buildings
ArcGISSceneLayerto the scene’s operational layers and set its visibility tofalseso it does not intersect the 3D basemap buildings. - Construct a
SceneLayerPolygonFilterwith the extent of the San Francisco Buildings Scene Layer and theSceneLayerPolygonFilterSpatialRelationship::Disjointenum to hide all features within the extent. - Set the
SceneLayerPolygonFilteron the OSM Buildings layer to hide all OSM buildings within the extent of the San Francisco Buildings layer. - Set the visibility of the 3D San Francisco Buildings layer to
trueto show the 3D buildings in the extent.
Relevant API
- ArcGISSceneLayer
- SceneLayerPolygonFilter
- SceneLayerPolygonFilterSpatialRelationship
About the data
This sample uses the Navigation 3D Basemap which includes commercial 3D buildings data acquired from TomTom and Vantor, in addition to Esri Community Maps and Overture Maps Foundation data. It also uses the San Francisco 3D Buildings scene layer which provides detailed 3D models of buildings in San Francisco, California, USA.
Additional information
This sample uses SceneLayerPolygonFilterSpatialRelationship::Disjoint to hide all features within the extent of the given geometry. You can alternatively use SceneLayerPolygonFilterSpatialRelationship::Contains to only show features within the extent of the geometry.
You can also show or hide features in a scene layer using ArcGISSceneLayer::setFeatureVisible or setFeaturesVisible and pass in a feature or list of features and a boolean value to set their visibility.
Tags
3D, buildings, disjoint, exclude, extent, filter, hide, OSM, polygon
Sample Code
// [WriteFile Name=FilterFeaturesInScene, Category=Scenes]// [Legal]// Copyright 2023 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "FilterFeaturesInScene.h"
// ArcGIS Maps SDK headers#include "ArcGISSceneLayer.h"#include "ArcGISTiledElevationSource.h"#include "Basemap.h"#include "Camera.h"#include "ElevationSourceListModel.h"#include "Envelope.h"#include "Error.h"#include "Graphic.h"#include "GraphicListModel.h"#include "GraphicsOverlay.h"#include "GraphicsOverlayListModel.h"#include "LayerListModel.h"#include "MapTypes.h"#include "Point.h"#include "Polygon.h"#include "PolygonBuilder.h"#include "Scene.h"#include "SceneLayerPolygonFilter.h"#include "SceneQuickView.h"#include "SimpleFillSymbol.h"#include "SimpleLineSymbol.h"#include "SpatialReference.h"#include "Surface.h"#include "SymbolTypes.h"
// Qt headers#include <QFuture>
using namespace Esri::ArcGISRuntime;
FilterFeaturesInScene::FilterFeaturesInScene(QObject* parent /* = nullptr */) : QObject(parent), m_scene(new Scene(this)){ // Construct and set the scene topography Surface* surface = new Surface(this); surface->elevationSources()->append( new ArcGISTiledElevationSource(QUrl("https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"), this)); m_scene->setBaseSurface(surface);
// Construct a basemap with the ArcGIS Navigation 3D Basemap then set it on the Scene Basemap* basemap = new Basemap(QUrl("https://arcgisruntime.maps.arcgis.com/home/item.html?id=00a5f468dda941d7bf0b51c144aae3f0"), this); m_scene->setBasemap(basemap);
connect(basemap, &Basemap::doneLoading, this, [basemap, this](const Error& error) { if (!error.isEmpty()) { qWarning() << "Basemap failed to load." << error.message() << error.additionalMessage(); return; }
// Loop through all layers in the basemap and look for the buildings layer for (Layer* baseLayer : *basemap->baseLayers()) { // As of 2025, the 3D basemap contains a layer called "Esri 3D Buildings" -- we'll search for a layer // that contains 'building' in case the name is ever updated if (baseLayer->name().contains("building", Qt::CaseInsensitive)) { m_3dBuildings = dynamic_cast<ArcGISSceneLayer*>(baseLayer); if (m_3dBuildings) { // Buildings layer found, we can return out of the lambda return; } } } if (!m_3dBuildings) { qWarning() << "Buildings layer not found in base layers. Please check that your basemap" << "contains an ArcGIS Scene Layer with 'building' in the name"; } });
// Construct the detailed buildings scene layer m_sanFrancisco3DOSceneLayer = new ArcGISSceneLayer(QUrl("https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/SanFrancisco_Bldgs/SceneServer"), this);
// Initially hide the detailed buildings so they don't clip into the 3D basemap buildings m_sanFrancisco3DOSceneLayer->setVisible(false);
// When the detailed building scene layer finishes loading, get its extent for the polygon filter connect(m_sanFrancisco3DOSceneLayer, &ArcGISSceneLayer::doneLoading, this, [this](const Error& error) { if (!error.isEmpty()) { qWarning() << "San Francisco Buildings Layer failed to load." << error.message() << error.additionalMessage(); return; }
// Construct a red polygon that shows the extent of the detailed buildings scene layer const Envelope sfExtent = m_sanFrancisco3DOSceneLayer->fullExtent();
PolygonBuilder* polygonBuilder = new PolygonBuilder(m_sceneView->spatialReference(), this); polygonBuilder->addPoint(sfExtent.xMin(), sfExtent.yMin()); polygonBuilder->addPoint(sfExtent.xMax(), sfExtent.yMin()); polygonBuilder->addPoint(sfExtent.xMax(), sfExtent.yMax()); polygonBuilder->addPoint(sfExtent.xMin(), sfExtent.yMax());
m_sceneLayerExtentPolygon = polygonBuilder->toPolygon();
// Create the SceneLayerPolygonFilter to later apply to the OSM buildings layer m_sceneLayerPolygonFilter = new SceneLayerPolygonFilter(QList<Polygon>{m_sceneLayerExtentPolygon}, // polygons to filter by SceneLayerPolygonFilterSpatialRelationship::Disjoint, // hide all features within the polygons this);
// Create the extent graphic so we can add it later with the detailed buildings scene layer SimpleFillSymbol* simpleFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle::Solid, QColor(Qt::transparent), new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, QColor(Qt::red), 5.0f, this), this); m_sanFranciscoExtentGraphic = new Graphic(m_sceneLayerExtentPolygon, simpleFillSymbol, this); });
m_scene->operationalLayers()->append(m_sanFrancisco3DOSceneLayer);}
FilterFeaturesInScene::~FilterFeaturesInScene() = default;
void FilterFeaturesInScene::init(){ // Register the scene view for QML qmlRegisterType<SceneQuickView>("Esri.Samples", 1, 0, "SceneView"); qmlRegisterType<FilterFeaturesInScene>("Esri.Samples", 1, 0, "FilterFeaturesInSceneSample");}
// Show the detailed buildings scene layervoid FilterFeaturesInScene::showDetailedBuildings(){ if (!m_sanFrancisco3DOSceneLayer) { qWarning() << "Detailed San Francisco Buildings layer not loaded"; return; } m_sanFrancisco3DOSceneLayer->setVisible(true);}
// Hide buildings within the detailed building extent so they don't clipvoid FilterFeaturesInScene::filterScene(){ if (!m_sceneView || !m_sceneView->graphicsOverlays()->first()) { return; }
// Show the extent graphic to visualize the polygon filter m_sceneView->graphicsOverlays()->first()->graphics()->append(m_sanFranciscoExtentGraphic);
if (!m_3dBuildings || !m_sceneLayerPolygonFilter) { return; }
// Initially, the building layer does not have a polygon filter, set it if (!m_3dBuildings->polygonFilter()) { m_3dBuildings->setPolygonFilter(m_sceneLayerPolygonFilter); }
// After the scene is reset, the layer will have a polygon filter, but that filter will not have polygons set // Add the polygon back to the polygon filter else { m_sceneLayerPolygonFilter->setPolygons({m_sceneLayerExtentPolygon}); }}
void FilterFeaturesInScene::reset(){ if (!m_sanFrancisco3DOSceneLayer) { return; }
// Hide the detailed buildings layer from the scene m_sanFrancisco3DOSceneLayer->setVisible(false);
if (!m_3dBuildings || !m_3dBuildings->polygonFilter()) { return; } // Set the 3D buildings polygon filter to an empty list of polygons to clear the filter m_3dBuildings->polygonFilter()->setPolygons(QList<Polygon>{});
if (!m_sceneView || !m_sceneView->graphicsOverlays()->first()) { return; }
// Clear the graphics list in the graphics overlay to remove the red extent boundary graphic m_sceneView->graphicsOverlays()->first()->graphics()->clear();}
SceneQuickView* FilterFeaturesInScene::sceneView() const{ return m_sceneView;}
// Set the view (created in QML)void FilterFeaturesInScene::setSceneView(SceneQuickView* sceneView){ if (!sceneView || sceneView == m_sceneView) { return; }
m_sceneView = sceneView; m_sceneView->setArcGISScene(m_scene);
// Set the viewpoint to San Francisco m_sceneView->setViewpointCameraAsync(Camera(Point(-122.421, 37.7041, 207), 60, 70, 0));
// Add a graphics overlay to display the extent graphic m_sceneView->graphicsOverlays()->append(new GraphicsOverlay(this));
emit sceneViewChanged();}// [WriteFile Name=FilterFeaturesInScene, Category=Scenes]// [Legal]// Copyright 2023 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef FILTERFEATURESINSCENE_H#define FILTERFEATURESINSCENE_H
// ArcGIS Maps SDK headers#include "Polygon.h"
// Qt headers#include <QObject>
namespace Esri::ArcGISRuntime{ class ArcGISSceneLayer; class Graphic; class Scene; class SceneLayerPolygonFilter; class SceneQuickView;} // namespace Esri::ArcGISRuntime
Q_MOC_INCLUDE("SceneQuickView.h");
class FilterFeaturesInScene : public QObject{ Q_OBJECT
Q_PROPERTY(Esri::ArcGISRuntime::SceneQuickView* sceneView READ sceneView WRITE setSceneView NOTIFY sceneViewChanged)
public: explicit FilterFeaturesInScene(QObject* parent = nullptr); ~FilterFeaturesInScene() override;
static void init();
Q_INVOKABLE void showDetailedBuildings(); Q_INVOKABLE void filterScene(); Q_INVOKABLE void reset();
signals: void sceneViewChanged();
private: Esri::ArcGISRuntime::SceneQuickView* sceneView() const; void setSceneView(Esri::ArcGISRuntime::SceneQuickView* sceneView);
Esri::ArcGISRuntime::Scene* m_scene = nullptr; Esri::ArcGISRuntime::SceneQuickView* m_sceneView = nullptr; Esri::ArcGISRuntime::ArcGISSceneLayer* m_3dBuildings = nullptr; Esri::ArcGISRuntime::ArcGISSceneLayer* m_sanFrancisco3DOSceneLayer = nullptr; Esri::ArcGISRuntime::Polygon m_sceneLayerExtentPolygon; Esri::ArcGISRuntime::Graphic* m_sanFranciscoExtentGraphic = nullptr; Esri::ArcGISRuntime::SceneLayerPolygonFilter* m_sceneLayerPolygonFilter = nullptr;};
#endif // FILTERFEATURESINSCENE_H// [WriteFile Name=FilterFeaturesInScene, Category=Scenes]// [Legal]// Copyright 2023 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuickimport QtQuick.Controlsimport Esri.Samples
Item { // add a SceneView component SceneView { id: view anchors.fill: parent
Component.onCompleted: { // Set and keep the focus on SceneView to enable keyboard navigation forceActiveFocus(); } }
// Declare the C++ instance which creates the scene etc. and supply the view FilterFeaturesInSceneSample { id: model sceneView: view }
Button { anchors { top: parent.top right: parent.right margins: 15 }
property int step: 0
text: [qsTr("Filter 3D buildings in extent"),qsTr("Show detailed buildings"),qsTr("Reset scene")][step] onClicked: { switch (step) { case 0: // Hide buildings within the detailed building extent so they don't clip model.filterScene(); step++; break;
case 1: // Show the detailed buildings scene layer and extent graphic model.showDetailedBuildings(); step++; break;
case 2: // Reset the scene to its original state model.reset(); step = 0; break;
default: step = 0; break; } } }}// [Legal]// Copyright 2023 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "FilterFeaturesInScene.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QCommandLineParser>#include <QDir>#include <QGuiApplication>#include <QQmlApplicationEngine>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
int main(int argc, char* argv[]){ QGuiApplication app(argc, argv); app.setApplicationName(QString("FilterFeaturesInScene"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token.
const QString accessToken = QString("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample FilterFeaturesInScene::init();
// Initialize application view QQmlApplicationEngine engine; // Add the import Path engine.addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
#ifdef ARCGIS_RUNTIME_IMPORT_PATH_2 engine.addImportPath(ARCGIS_RUNTIME_IMPORT_PATH_2);#endif
// Set the source engine.load(QUrl("qrc:/Samples/Scenes/FilterFeaturesInScene/main.qml"));
return app.exec();}// Copyright 2023 Esri.
// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
import QtQuick.Controlsimport Esri.Samples
ApplicationWindow { visible: true width: 800 height: 600
FilterFeaturesInScene { anchors.fill: parent }}