Skip to content
View on GitHubSample viewer app

Change the appearance of a 3D object scene layer with different renderers.

Screenshot of apply renderers to scene layer sample

Use case

A scene layer of 3D buildings hosted on ArcGIS Online comes with a preset renderer that defines how the buildings are displayed in the application. However, the fill color may sometimes blend into the basemap, making the buildings difficult to distinguish. To enhance visualization, you can apply a custom renderer with a more contrasting fill color, helping the 3D buildings stand out more clearly. Additionally, you can use a unique value renderer to represent different building uses, or a class breaks renderer to visualize building ages - valuable insights for urban planning and analysis.

How to use the sample

Wait for the scene layer to load. The original scene layer displays 3D textured buildings. Tap on the "Select Renderer" dropdown menu and choose a different renderer to change how the buildings are visualized. Each renderer applies different symbology to the scene layer. Setting the renderer to null will remove any applied symbology, reverting the buildings to their original textured appearance.

How it works

  1. Create an ArcGISSceneLayer from a service URL.
  2. Add the scene layer to an ArcGISScene and display it in a SceneView.
  3. Create different renderers:
    • A SimpleRenderer with a MultilayerMeshSymbol and a fill color and edges.
    • A UniqueValueRenderer using a string field and different MultilayerMeshSymbol for each unique value of the building usage.
    • A ClassBreaksRenderer using a numeric field and different MultilayerMeshSymbol for each value range of the year the building was completed.
  4. Set the scene layer's renderer property to the selected renderer.
  5. Set the scene layer's renderer property to null, resulting in displaying the original texture of the buildings.

Relevant API

  • ArcGISSceneLayer
  • ClassBreaksRenderer
  • MaterialFillSymbolLayer
  • MultilayerMeshSymbol
  • SceneView
  • SimpleRenderer
  • SymbolLayerEdges3D
  • UniqueValueRenderer

About the data

This sample displays a Helsinki 3D buildings scene hosted on ArcGIS Online, showing 3D textured buildings in Helsinki, Finland.

Tags

3D, buildings, renderer, scene layer, symbology, visualization

Sample Code

ApplyRenderersToSceneLayerViewModel.ktApplyRenderersToSceneLayerViewModel.ktMainActivity.ktApplyRenderersToSceneLayerScreen.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
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/* 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.applyrendererstoscenelayer.components

import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.arcgismaps.Color
import com.arcgismaps.geometry.Point
import com.arcgismaps.geometry.SpatialReference
import com.arcgismaps.mapping.ArcGISScene
import com.arcgismaps.mapping.ArcGISTiledElevationSource
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.Surface
import com.arcgismaps.mapping.Viewpoint
import com.arcgismaps.mapping.layers.ArcGISSceneLayer
import com.arcgismaps.mapping.symbology.ClassBreak
import com.arcgismaps.mapping.symbology.ClassBreaksRenderer
import com.arcgismaps.mapping.symbology.ColorMixMode
import com.arcgismaps.mapping.symbology.MaterialFillSymbolLayer
import com.arcgismaps.mapping.symbology.MultilayerMeshSymbol
import com.arcgismaps.mapping.symbology.SimpleRenderer
import com.arcgismaps.mapping.symbology.SymbolLayerEdges3D
import com.arcgismaps.mapping.symbology.UniqueValue
import com.arcgismaps.mapping.symbology.UniqueValueRenderer
import com.arcgismaps.mapping.view.Camera
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

/**
 * ViewModel for the ApplyRenderersToSceneLayer sample.
 * Displays a 3D buildings scene layer and allows switching between different renderers.
 */
class ApplyRenderersToSceneLayerViewModel(app: Application) : AndroidViewModel(app) {
    // URLs for the Helsinki buildings scene and world elevation
    private val helsinkiBuildingsSceneLayerUrl =
        "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Helsinki_buildings/SceneServer"
    private val worldElevationServiceUrl =
        "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"

    // ArcGISTiledElevationSource for world elevation
    private val elevationSource = ArcGISTiledElevationSource(worldElevationServiceUrl)

    // ArcGISSceneLayer for Helsinki buildings
    val sceneLayer = ArcGISSceneLayer(helsinkiBuildingsSceneLayerUrl)

    // Camera location for Helsinki
    private val cameraLocation = Point(
        x = 2778453.8008,
        y = 8436451.3882,
        z = 387.4524,
        spatialReference = SpatialReference.webMercator()
    )

    // Camera to view the scene
    private val camera = Camera(
        locationPoint = cameraLocation,
        heading = 308.9,
        pitch = 50.7,
        roll = 0.0
    )

    // Create the ArcGISScene with light gray basemap
    val arcGISScene by mutableStateOf(
        ArcGISScene(BasemapStyle.ArcGISLightGray).apply {
            // Add the Helsinki buildings scene layer
            operationalLayers.add(sceneLayer)
            // Add the 3D elevation source to the base surface
            baseSurface = Surface().apply {
                elevationSources.add(elevationSource)
            }
            // Set the viewpoint camera at Helsinki
            initialViewpoint = Viewpoint(
                center = cameraLocation,
                camera = camera,
                scale = 1e4
            )
        }
    )

    // Renderer selection state
    val rendererTypes = listOf(
        RendererType.SimpleRenderer,
        RendererType.UniqueValueRenderer,
        RendererType.ClassBreaksRenderer,
        RendererType.NullRenderer
    )
    // Create a flow to keep track of the current selected renderer type. It is initialized as SimpleRenderer.
    private val _selectedRendererType = MutableStateFlow(RendererType.SimpleRenderer)
    // Keep track of the current selected renderer type
    val selectedRendererType = _selectedRendererType.asStateFlow()

    // Simple renderer created from a multilayer mesh symbol with a material fill symbol layer
    // The colorMixMode of the material fill symbol layer is Replace which will replace the texture with the new color.
    // Edges are also set.
    private val simpleRenderer : SimpleRenderer by lazy {
        SimpleRenderer(
            symbol = MultilayerMeshSymbol(
                symbolLayer = MaterialFillSymbolLayer(
                    color = Color.yellow
                ).apply {
                    colorMixMode= ColorMixMode.Replace
                    edges = SymbolLayerEdges3D(
                        color = Color.black,
                        width = 0.5
                    )
                }
            )
        )
    }

    // Unique value renderer using multilayer mesh symbols based on usage of the building
    // The material fill symbol layers use the default colorMixMode which is Multiply.
    // Multiply colorMixMode will multiply the initial texture color with the new color.
    private val uniqueValueRenderer : UniqueValueRenderer by lazy {
        UniqueValueRenderer(
            fieldNames = listOf("usage"),
            defaultSymbol = MultilayerMeshSymbol(
                symbolLayer = MaterialFillSymbolLayer(
                    color = Color.fromRgba(230, 230, 230, 255)
                )
            ),
            uniqueValues = listOf(
                UniqueValue(
                    description = "commercial buildings",
                    label = "commercial buildings",
                    symbol = MultilayerMeshSymbol(
                        symbolLayer = MaterialFillSymbolLayer(
                            color = Color.fromRgba(245, 213, 169, 200)
                        )
                    ),
                    values = listOf("general or commercial")
                ),
                UniqueValue(
                    description = "residential buildings",
                    label = "residential buildings",
                    symbol = MultilayerMeshSymbol(
                        symbolLayer = MaterialFillSymbolLayer(
                            color = Color.fromRgba(210, 254, 208, 255)
                        )
                    ),
                    values = listOf("residential")
                ),
                UniqueValue(
                    description = "other",
                    label = "other",
                    symbol = MultilayerMeshSymbol(
                        symbolLayer = MaterialFillSymbolLayer(
                            color = Color.fromRgba(253, 198, 227, 150)
                        )
                    ),
                    values = listOf("other")
                )
            )
        )
    }

    // Class breaks renderer using multilayer mesh symbols based on year completed of the building
    // The colorMixMode used in the material fill symbol layer is Tint which will set the new color on the desaturated texture.
    private val classBreaksRenderer : ClassBreaksRenderer by lazy {
        ClassBreaksRenderer(
            fieldName = "yearCompleted",
            classBreaks = listOf(
                ClassBreak(
                    description = "before 1900",
                    label = "before 1900",
                    minValue = 1725.0,
                    maxValue = 1899.0,
                    symbol = MultilayerMeshSymbol(
                        symbolLayer = MaterialFillSymbolLayer(
                            color = Color.fromRgba(230, 238, 207, 255)
                        ).apply { colorMixMode = ColorMixMode.Tint }
                    )
                ),
                ClassBreak(
                    description = "1900 - 1956",
                    label = "1900 - 1956",
                    minValue = 1900.0,
                    maxValue = 1956.0,
                    symbol = MultilayerMeshSymbol(
                        symbolLayer = MaterialFillSymbolLayer(
                            color = Color.fromRgba(155, 196, 193, 255)
                        ).apply { colorMixMode = ColorMixMode.Tint }
                    )
                ),
                ClassBreak(
                    description = "1957 - 2000",
                    label = "1957 - 2000",
                    minValue = 1957.0,
                    maxValue = 2000.0,
                    symbol = MultilayerMeshSymbol(
                        symbolLayer = MaterialFillSymbolLayer(
                            color = Color.fromRgba(105, 168, 183, 255)
                        ).apply { colorMixMode = ColorMixMode.Tint }
                    )
                ),
                ClassBreak(
                    description = "after 2000",
                    label = "after 2000",
                    minValue = 2001.0,
                    maxValue = 3000.0,
                    symbol = MultilayerMeshSymbol(
                        symbolLayer = MaterialFillSymbolLayer(
                            color = Color.fromRgba(75, 126, 152, 255)
                        ).apply { colorMixMode = ColorMixMode.Tint }
                    )
                )
            )
        )
    }

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

    init {
        // Load the scene
        viewModelScope.launch {
            // Apply simple renderer to the scene layer as the initial renderer
            sceneLayer.renderer = simpleRenderer
            arcGISScene.load().onFailure { messageDialogVM.showMessageDialog(it) }
        }
    }

    /**
     * Switches the selected renderer according to the renderer type.
     */
    fun updateSceneLayerRenderer(rendererType: RendererType) {
        _selectedRendererType.value = rendererType
        sceneLayer.renderer = when (rendererType) {
            RendererType.SimpleRenderer -> {
                simpleRenderer
            }
            RendererType.UniqueValueRenderer -> {
                uniqueValueRenderer
            }
            RendererType.ClassBreaksRenderer -> {
                classBreaksRenderer
            }
            RendererType.NullRenderer -> {
                null
            }
        }
    }

    /**
     * Enum representing the different renderer types.
     */
    enum class RendererType(val label: String) {
        SimpleRenderer("SimpleRenderer - Buildings without texture"),
        UniqueValueRenderer("UniqueValueRenderer - Buildings by usage"),
        ClassBreaksRenderer("ClassBreaksRenderer - Buildings by year completed"),
        NullRenderer("Null renderer - Buildings with original texture")
    }
}

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