Skip to content
View on GitHubSample viewer app

Explore details of a building scene by using filters and sublayer visibility.

Image of a building scene layer

Use case

Buildings and their component parts (in this example, structural, electrical, or architectural) can be difficult to explain and visualize. An architectural firm might share a 3D building model visualization with clients and contractors to let them explore these components by floor and component type.

How to use the sample

In the filter controls, select floor and category options to filter what parts of the Building Scene Layer are displayed in the scene. Click on any of the building features to identify them.

How it works

  1. Create an ArcGISScene with the URL to a Building Scene Layer service.
  2. Create a LocalSceneView and add the scene.
  3. Retrieve the BuildingSceneLayer from the scene's operational layers.
  4. Click the floating action button to view the filtering options.
  5. Select a floor from the "Floor" dropdown to view the internal details of each floor or "All" to view the entire model.
  6. Expand the categories to show or hide individual items in the building model. The entire category may be shown or hidden as well.
  7. Click on any of the building features to view the attributes of the feature.

Relevant API

  • ArcGISScene
  • BuildingComponentSublayer
  • BuildingFilter
  • BuildingFilterBlock
  • BuildingSceneLayer
  • LocalSceneView
  • Popup

About the data

This sample uses the Esri Building E Local Scene web scene, which contains a Building Scene Layer representing Building E on the Esri Campus in Redlands, CA. The Revit BIM model was brought into ArcGIS using the BIM capabilities in ArcGIS Pro and published to the web as a Building Scene Layer.

Additional information

Buildings in a Building Scene Layer can be very complex models composed of sublayers containing internal and external features of the structure. Sublayers may include structural components like columns, architectural components like floors and windows, and electrical components.

Applying filters to the Building Scene Layer can highlight features of interest in the model. Filters are made up of filter blocks, which contain several properties that allow control over the filter's function. Setting the filter mode to X-Ray, for instance, will render features with a semi-transparent white color so other interior features can be seen. In addition, toggling the visibility of sublayers can show or hide all the features of a sublayer.

This sample uses the Popup toolkit component. For information about setting up the toolkit, as well as code for the underlying component, visit the toolkit docs.

Tags

3D, building scene layer, layers, popup, toolkit

Sample Code

FilterBuildingSceneLayerViewModel.ktFilterBuildingSceneLayerViewModel.ktMainActivity.ktFilterBuildingSceneLayerScreen.kt
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
/* Copyright 2025 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.
 *
 */

package com.esri.arcgismaps.sample.filterbuildingscenelayer.components

import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.arcgismaps.mapping.ArcGISScene
import com.arcgismaps.mapping.layers.BuildingSceneLayer
import com.arcgismaps.mapping.layers.buildingscene.BuildingComponentSublayer
import com.arcgismaps.mapping.layers.buildingscene.BuildingFilter
import com.arcgismaps.mapping.layers.buildingscene.BuildingFilterBlock
import com.arcgismaps.mapping.layers.buildingscene.BuildingGroupSublayer
import com.arcgismaps.mapping.layers.buildingscene.BuildingSolidFilterMode
import com.arcgismaps.mapping.layers.buildingscene.BuildingSublayer
import com.arcgismaps.mapping.layers.buildingscene.BuildingXrayFilterMode
import com.arcgismaps.mapping.popup.Popup
import com.arcgismaps.toolkit.popup.PopupState
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

class FilterBuildingSceneLayerViewModel(app: Application) : AndroidViewModel(app) {
    val scene = ArcGISScene("https://www.arcgis.com/home/item.html?id=b7c387d599a84a50aafaece5ca139d44")

    // State to control if a loading progress indicator is shown
    private val _showLoadingDialog = MutableStateFlow(true)
    val showLoadingDialog = _showLoadingDialog.asStateFlow()

    // Building scene layer that will be filtered. Set after the WebScene is loaded.
    var buildingSceneLayer: BuildingSceneLayer? = null

    // The selected floor
    var selectedFloor by mutableStateOf("All")

    // The list of available floors
    val floors: MutableList<String> = mutableListOf(selectedFloor)

    // The list of building sublayer categories
    val categories: MutableList<BuildingSublayer> = mutableListOf()

    // Create a message dialog view model for handling error messages
    val messageDialogVM = MessageDialogViewModel()

    // Building scene layer sublayer that contains the currently selected feature
    var sublayerWithSelection : BuildingComponentSublayer? = null

    // State that will contain a popup state for an identify result
    private val _popupState = MutableStateFlow<PopupState?>(null)
    val popupState = _popupState.asStateFlow()

    init {
        viewModelScope.launch {
            scene.load().onFailure {
                messageDialogVM.showMessageDialog(it)
                _showLoadingDialog.value = false
            }.onSuccess {
                _showLoadingDialog.value = false

                buildingSceneLayer =
                    scene.operationalLayers.first { layer ->
                        layer is BuildingSceneLayer
                    } as BuildingSceneLayer

                buildingSceneLayer?.let { buildingSceneLayer ->
                    // Get the floor listing from the statistics
                    buildingSceneLayer.fetchStatistics().onSuccess { statistics ->
                        statistics["BldgLevel"]?.mostFrequentValues?.let {
                            floors.addAll(0, it.sorted())
                        }

                        // The top-level sublayer groups will be the categories
                        buildingSceneLayer.sublayers.find { sublayer ->
                            sublayer.modelName == "FullModel"
                        }?.let { buildingSublayer ->
                            buildingSublayer as BuildingGroupSublayer
                            categories.addAll(buildingSublayer.sublayers.sortedBy { it.name })
                        }
                    }
                }
            }
        }
    }

    /**
     * Updates the building filters based on the selected floor
     */
    fun selectFloor(index: Int) {
        selectedFloor = floors[index]

        buildingSceneLayer?.let { buildingSceneLayer ->
            if (selectedFloor == "All") {
                // No filtering applied if 'All' floors are selected
                buildingSceneLayer.activeFilter = null
                return
            }
            // Build a building filter to show the selected floor and an xray view of the floors below.
            // Floors above the selected floor are not shown at all.
            val buildingFilter = BuildingFilter(
                name = "Floor filter",
                description = "Show selected floor and xray filter for lower floors.",
                listOf(
                    BuildingFilterBlock(
                        title = "solid block",
                        whereClause = "BldgLevel = $selectedFloor",
                        mode = BuildingSolidFilterMode()
                    ),
                    BuildingFilterBlock(
                        title = "x ray block",
                        whereClause = "BldgLevel < $selectedFloor",
                        mode = BuildingXrayFilterMode()
                    )
                )
            )
            buildingSceneLayer.activeFilter = buildingFilter
        }
    }

    /**
     * Creates a popup state to display identify result
     */
    fun createPopupState(popup: Popup) {
        _popupState.value = PopupState(popup = popup, scope = viewModelScope)
    }

    /**
     * Dismisses an identify result
     */
    fun dismissPopup() {
        _popupState.value = null
    }
}

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