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. Tap 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. Tap the "Show detailed buildings" button to load a scene layer that contains more detailed buildings. Tap 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 3D buildings layer to hide all 3D 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 the SceneLayerPolygonFilterSpatialRelationship.Contains enum 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
// 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.
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.UI;
namespace ArcGIS.Samples.FilterFeaturesInScene
{
[ArcGIS.Samples.Shared.Attributes.Sample(
name: "Filter features in scene",
category: "Scene",
description: "Filter 3D scene features out of a given geometry with a polygon filter.",
instructions: "The sample initializes showing the \"Navigation\" 3D Basemap. Tap 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. Tap the \"Show detailed buildings\" button to load a scene layer that contains more detailed buildings. Tap the \"Reset scene\" button to hide the detailed buildings scene layer and clear the 3D buildings filter.",
tags: new[] { "3D", "OSM", "buildings", "disjoint", "exclude", "extent", "filter", "hide", "polygon" })]
[ArcGIS.Samples.Shared.Attributes.OfflineData()]
public partial class FilterFeaturesInScene
{
// ArcGIS Online services.
private const string Basemap3D = "https://www.arcgis.com/home/item.html?id=00a5f468dda941d7bf0b51c144aae3f0";
private const string ElevationSource = "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer";
private const string DetailedBuildings = "https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/SanFrancisco_Bldgs/SceneServer";
private ArcGISSceneLayer _detailedBuildingsSceneLayer;
private ArcGISSceneLayer _3dBuildingsSceneLayer;
private SceneLayerPolygonFilter _sceneLayerPolygonFilter;
private Graphic _cityExtentGraphic;
private Polygon _sceneLayerExtentPolygon;
// String to keep track of which step user is on.
private string _step;
public FilterFeaturesInScene()
{
InitializeComponent();
_ = Initialize();
}
private async Task Initialize()
{
MySceneView.Scene = new Scene();
// Create a surface with an elevation source for the scene.
var surface = new Surface();
surface.ElevationSources.Add(new ArcGISTiledElevationSource(new Uri(ElevationSource)));
MySceneView.Scene.BaseSurface = surface;
// Create and set the basemap using a 3D basemap portal item.
var basemap = new Basemap(new Uri(Basemap3D));
MySceneView.Scene.Basemap = basemap;
// Wait for basemap to load to find the 3D buildings layer.
await basemap.LoadAsync();
// Loop through all layers in the basemap and look for the buildings layer.
foreach (var baseLayer in basemap.BaseLayers)
{
// Search for a layer that contains 'building' in case the name is ever updated.
if (baseLayer.Name.Contains("building", StringComparison.OrdinalIgnoreCase))
{
_3dBuildingsSceneLayer = baseLayer as ArcGISSceneLayer;
if (_3dBuildingsSceneLayer != null)
{
break;
}
}
}
if (_3dBuildingsSceneLayer == null)
{
await Application.Current.Windows[0].Page.DisplayAlert("Error", "Buildings layer not found in base layers. Please check that your basemap contains an ArcGIS Scene Layer with 'building' in the name.", "OK");
return;
}
// Create a scene layer for the detailed buildings.
_detailedBuildingsSceneLayer = new ArcGISSceneLayer(new Uri(DetailedBuildings));
// Initially hide the detailed buildings so they don't clip into the 3D basemap buildings.
_detailedBuildingsSceneLayer.IsVisible = false;
// Add the detailed buildings layer to the scene's operational layers.
MySceneView.Scene.OperationalLayers.Add(_detailedBuildingsSceneLayer);
// When the detailed building scene layer finishes loading, get its extent for the polygon filter.
await _detailedBuildingsSceneLayer.LoadAsync();
// Construct a red polygon that shows the extent of the detailed buildings scene layer.
Envelope cityExtent = _detailedBuildingsSceneLayer.FullExtent;
var builder = new PolygonBuilder(MySceneView.SpatialReference);
builder.AddPoint(cityExtent.XMin, cityExtent.YMin);
builder.AddPoint(cityExtent.XMax, cityExtent.YMin);
builder.AddPoint(cityExtent.XMax, cityExtent.YMax);
builder.AddPoint(cityExtent.XMin, cityExtent.YMax);
_sceneLayerExtentPolygon = builder.ToGeometry();
// Create the SceneLayerPolygonFilter to later apply to the 3D buildings layer.
_sceneLayerPolygonFilter = new SceneLayerPolygonFilter(new List<Polygon>() { _sceneLayerExtentPolygon }, SceneLayerPolygonFilterSpatialRelationship.Disjoint);
// Create the extent graphic so we can add it later with the detailed buildings scene layer.
var simpleLineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.Red, 5.0f);
var simpleFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Transparent, simpleLineSymbol);
_cityExtentGraphic = new Graphic(_sceneLayerExtentPolygon, simpleFillSymbol);
// Initially hide the graphic, since the filter has not been applied yet.
_cityExtentGraphic.IsVisible = false;
MySceneView.GraphicsOverlays.Add(new GraphicsOverlay());
MySceneView.GraphicsOverlays.First().Graphics.Add(_cityExtentGraphic);
// Set up initial button state.
_step = "FilterScene";
MyButton.Text = "Filter 3D buildings in extent";
await MySceneView.SetViewpointCameraAsync(new Camera(new MapPoint(-122.421, 37.7041, 207), 60, 70, 0));
}
// Determine which step of the sample user is on.
private void MyButton_Click(object sender, EventArgs e)
{
switch (_step)
{
case "FilterScene":
FilterScene();
break;
case "ShowDetailedBuildings":
ShowDetailedBuildings();
break;
case "ResetScene":
ResetScene();
break;
}
}
// Hide buildings within the detailed building extent so they don't clip.
private void FilterScene()
{
// Update step and label to reflect next step.
_step = "ShowDetailedBuildings";
MyButton.Text = "Show detailed buildings";
// Apply the polygon filter to the buildings layer.
_3dBuildingsSceneLayer.PolygonFilter = _sceneLayerPolygonFilter;
// Show the extent graphic to visualize the polygon filter.
_cityExtentGraphic.IsVisible = true;
}
// Show the detailed buildings scene layer.
private void ShowDetailedBuildings()
{
// Update step and label to reflect next step.
_step = "ResetScene";
MyButton.Text = "Reset scene";
// Show the detailed buildings scene layer.
_detailedBuildingsSceneLayer.IsVisible = true;
}
// Reset the scene to its original state.
private void ResetScene()
{
// Update step and label to reflect next step.
_step = "FilterScene";
MyButton.Text = "Filter 3D buildings in extent";
// Hide the detailed buildings layer from the scene.
_detailedBuildingsSceneLayer.IsVisible = false;
// Remove the polygon filter from the buildings layer.
_3dBuildingsSceneLayer.PolygonFilter = null;
// Hide the red extent boundary graphic.
_cityExtentGraphic.IsVisible = false;
}
}
}