Skip to content
View on GitHubSample viewer app

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

screenshot

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

  1. Create a Surface for the scene and set the World Elevation 3D as an elevation source.
  2. Construct a Basemap for the scene using the "Navigation" 3D Basemap, load it, then search for the "buildings" baselayer.
  3. Add the 3D San Francisco Buildings ArcGISSceneLayer to the scene's operational layers and set its visibility to false so it does not intersect the 3D basemap buildings.
  4. Construct a SceneLayerPolygonFilter with the extent of the San Francisco Buildings Scene Layer and the SceneLayerPolygonFilterSpatialRelationship::Disjoint enum to hide all features within the extent.
  5. Set the SceneLayerPolygonFilter on the OSM Buildings layer to hide all OSM buildings within the extent of the San Francisco Buildings layer.
  6. Set the visibility of the 3D San Francisco Buildings layer to true to 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

FilterFeaturesInScene.cppFilterFeaturesInScene.cppFilterFeaturesInScene.hFilterFeaturesInScene.qml
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// [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 layer
void 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 clip
void 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();
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.