Skip to content
View on GitHubSample viewer app

Extrude graphics based on an attribute value.

Image of show extruded graphics

Use case

Graphics representing features can be vertically extruded to represent properties of the data that might otherwise go unseen. Extrusion can add visual prominence to data beyond what may be offered by varying the color, size, or shape of symbol alone. For example, graphics representing wind turbines in a wind farm application can be extruded by a real-world "height" attribute so that they can be visualized in a landscape. Likewise, census data can be extruded by a thematic "population" attribute to visually convey population levels across a country.

How to use the sample

Run the sample. Note that the graphics are extruded to the level set in their height property.

How it works

  1. Create a GraphicsOverlay and SimpleRenderer.
  2. Set the extrusion mode for the renderer with RendererSceneProperties.extrusionMode = ExtrusionMode.BaseHeight.
  3. Specify the attribute name of the graphic that the extrusion mode will use, RendererSceneProperties.extrusionExpression = "[height]".
  4. Set the renderer on the graphics overlay using, GraphicsOverlay.renderer.
  5. Create graphics with their attribute set, graphic.attributes["height"].

Relevant API

  • ExtrusionMode
  • RendererSceneProperties
  • SimpleRenderer

About the data

Data is hard coded in this sample as a demonstration of how to create and set an attribute to a graphic. To extrude graphics based on pre-existing attributes (e.g. from a feature layer) see the FeatureLayerExtrusion sample.

Tags

3D, extrude, extrusion, height, scene, visualization

Sample Code

ShowExtrudedGraphicsViewModel.ktShowExtrudedGraphicsViewModel.ktMainActivity.ktShowExtrudedGraphicsScreen.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
/* 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.showextrudedgraphics.components

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.arcgismaps.Color
import com.arcgismaps.geometry.Point
import com.arcgismaps.geometry.PolygonBuilder
import com.arcgismaps.mapping.ArcGISScene
import com.arcgismaps.mapping.ArcGISTiledElevationSource
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.Viewpoint
import com.arcgismaps.mapping.symbology.ExtrusionMode
import com.arcgismaps.mapping.view.GraphicsOverlay
import com.arcgismaps.mapping.view.SurfacePlacement
import com.arcgismaps.mapping.view.Graphic
import com.arcgismaps.mapping.symbology.SimpleFillSymbol
import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
import com.arcgismaps.mapping.symbology.SimpleLineSymbol
import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
import com.arcgismaps.mapping.symbology.SimpleRenderer
import com.arcgismaps.mapping.symbology.RendererSceneProperties
import com.arcgismaps.mapping.view.Camera
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
import kotlinx.coroutines.launch
import kotlin.random.Random

/**
 * ViewModel for the ShowExtrudedGraphics sample.
 *
 * Prepares a 3D scene, adds an elevation source and a graphics overlay whose
 * renderer is configured to extrude graphics based on the "height" attribute.
 */
class ShowExtrudedGraphicsViewModel(application: Application) : AndroidViewModel(application) {

    // Create a camera and set the scene's initial viewpoint.
    private val camera = Camera(
        latitude = 28.4,
        longitude = 83.0,
        altitude = 20_000.0,
        heading = 10.0,
        pitch = 70.0,
        roll = 0.0
    )
    // A 3D scene with a topographic basemap and an initial camera viewpoint.
    val arcGISScene = ArcGISScene(BasemapStyle.ArcGISTopographic).apply {

        initialViewpoint = Viewpoint(camera = camera, boundingGeometry = camera.location)

        // Add a global elevation source to the base surface so extruded geometry has context.
        baseSurface.elevationSources.add(
            ArcGISTiledElevationSource(
                uri = WORLD_ELEVATION_SERVICE_URL
            )
        )
    }


    // Graphics overlay that will contain extruded polygon graphics.
    val graphicsOverlay: GraphicsOverlay

    // Message dialog view model for reporting load errors.
    val messageDialogVM = MessageDialogViewModel()

    val squareSize = 0.01

    init {
        // Build renderer configured to extrude using the "height" attribute.
        val outline = SimpleLineSymbol(
            style = SimpleLineSymbolStyle.Solid,
            color = Color.white,
            width = 1f
        )

        val fillSymbol = SimpleFillSymbol(
            style = SimpleFillSymbolStyle.Solid,
            color = Color.fromRgba(220, 50, 50, 200),
            outline = outline
        )

        val renderer = SimpleRenderer(symbol = fillSymbol).apply {
            // Set extrusion mode and expression so the renderer uses the "height" attribute.
            sceneProperties = RendererSceneProperties().apply {
                extrusionMode = ExtrusionMode.BaseHeight
                extrusionExpression = "[height]"
            }
        }

        // Initialize the graphics overlay and assign the renderer.
        graphicsOverlay = GraphicsOverlay().apply {
            sceneProperties.surfacePlacement = SurfacePlacement.DrapedBillboarded
            this.renderer = renderer
        }

        // Populate the overlay with extruded graphics.
        addExtrudedGraphics()
        viewModelScope.launch {

            // Load the scene; if loading fails, show a message.
            arcGISScene.load().onFailure { messageDialogVM.showMessageDialog(it) }
        }
    }

    // Adds a grid of square polygons to the graphics overlay and assigns each a random height.
    private fun addExtrudedGraphics() {
        val baseX = camera.location.x - 0.01
        val baseY = camera.location.y + 0.25
        val spacing = 0.01
        val columns = 6
        val rows = 4
        val maxHeight = 10_000

        for (column in 0 until columns) {
            for (row in 0 until rows) {
                val startX = baseX + column * (squareSize + spacing)
                val startY = baseY + row * (squareSize + spacing)
                val polygon = polygonForPoint(startX, startY)
                val height = Random.nextInt(0, maxHeight + 1)
                val graphic = Graphic(geometry = polygon).apply {
                    // The renderer will use this attribute to extrude the graphic.
                    attributes["height"] = height
                }
                graphicsOverlay.graphics.add(graphic)
            }
        }
    }

    // Helper to construct a square polygon given a lower-left origin.
    private fun polygonForPoint(x: Double, y: Double) =
        PolygonBuilder().apply {
            addPoint(Point(x = x, y = y))
            addPoint(Point(x = x, y = y + squareSize))
            addPoint(Point(x = x + squareSize, y = y + squareSize))
            addPoint(Point(x = x + squareSize, y = y))
        }.toGeometry()

    companion object {
        // World elevation service used to provide base surface elevation.
        private const val WORLD_ELEVATION_SERVICE_URL =
            "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
    }
}

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