Show viewshed from point in scene

View on GitHubSample viewer app

Perform a viewshed analysis from a defined vantage point.

Image of viewshed location

Use case

A 3D viewshed analysis is a type of visual analysis you can perform on a scene. The viewshed shows what can be seen from a given location. The output is an overlay with two different colors - one representing the visible areas (green) and the other representing the obstructed areas (red). Viewshed analysis is a form of "exploratory analysis", which means the results are calculated on the current scale of the data, and the results are generated very quickly. If more "conclusive" results are required, consider using a GeoprocessingTask to perform a viewshed instead.

How to use the sample

Use the sliders to change the properties (heading, pitch, etc.), of the viewshed and see them updated in real time.

How it works

  1. Create a LocationViewshed passing in the observer location, heading, pitch, horizontal/vertical angles, and min/max distances.
  2. Set the property values on the viewshed instance for location, direction, range, and visibility properties.

Relevant API

  • AnalysisOverlay
  • ArcGISSceneLayer
  • ArcGISTiledElevationSource
  • LocationViewshed
  • Viewshed

About the data

The scene shows a buildings layer in Brest, France hosted on ArcGIS Online.

Additional information

This sample uses the GeoView-Compose Toolkit module to be able to implement a composable SceneView.

Tags

3D, frustum, geoview-compose, scene, viewshed, visibility analysis

Sample Code

SceneViewModel.ktSceneViewModel.ktMainActivity.ktMainScreen.ktViewshedOptionsScreen.ktViewshedSlider.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
/* 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.
 *
 */

package com.esri.arcgismaps.sample.showviewshedfrompointinscene.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 com.arcgismaps.analysis.LocationViewshed
import com.arcgismaps.geometry.Point
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.view.AnalysisOverlay
import com.arcgismaps.mapping.view.Camera
import com.arcgismaps.mapping.view.OrbitLocationCameraController
import com.esri.arcgismaps.sample.showviewshedfrompointinscene.R

class SceneViewModel(private val application: Application) : AndroidViewModel(application) {

    // initialize location viewshed parameters
    private var viewShed: LocationViewshed
    private val initHeading = 82.0
    private val initPitch = 60.0
    private val initHorizontalAngle = 75.0
    private val initVerticalAngle = 90.0
    private val initMinDistance = 0.0
    private val initMaxDistance = 1500.0

    private val initLocation = Point(
        x = -4.50,
        y = 48.4,
        z = 1000.0
    )
    private val camera = Camera(
        lookAtPoint = initLocation,
        distance = 20000000.0,
        heading = 0.0,
        pitch = 55.0,
        roll = 0.0
    )
    val cameraController = OrbitLocationCameraController(
        targetPoint = initLocation,
        distance = 5000.0
    )
    var scene by mutableStateOf(ArcGISScene(BasemapStyle.ArcGISNavigationNight))
    var analysisOverlay by mutableStateOf(AnalysisOverlay())


    init {
        // create a surface for elevation data
        val surface = Surface().apply {
            elevationSources.add(ArcGISTiledElevationSource(application.getString(R.string.elevation_service)))
        }

        // create a layer of buildings
        val buildingsSceneLayer = ArcGISSceneLayer(application.getString(R.string.buildings_layer))

        // create a scene and add imagery basemap, elevation surface, and buildings layer to it
        val buildingsScene = ArcGISScene(BasemapStyle.ArcGISImagery).apply {
            baseSurface = surface
            operationalLayers.add(buildingsSceneLayer)
        }

        val initLocation = Point(-4.50, 48.4, 1000.0)
        // create viewshed from the initial location
        viewShed = LocationViewshed(
            location = initLocation,
            heading = initHeading,
            pitch = initPitch,
            horizontalAngle = initHorizontalAngle,
            verticalAngle = initVerticalAngle,
            minDistance = initMinDistance,
            maxDistance = initMaxDistance
        ).apply {
            frustumOutlineVisible = true
        }

        // add the buildings scene to the sceneView
        scene = buildingsScene.apply {
            baseSurface = surface
            initialViewpoint = Viewpoint(initLocation, camera)
        }
        // add the viewshed to the analysisOverlay of the  scene view
        analysisOverlay.apply {
            analyses.add(viewShed)
            isVisible = true
        }
    }

    fun setHeading(sliderHeading: Float) {
        viewShed.heading = sliderHeading.toDouble()
    }

    fun setMaximumDistanceSlider(sliderValue: Float) {
        viewShed.maxDistance = sliderValue.toDouble()

    }

    fun setMinimumDistanceSlider(sliderValue: Float) {
        viewShed.minDistance = sliderValue.toDouble()
    }

    fun setVerticalAngleSlider(sliderValue: Float) {
        viewShed.verticalAngle = sliderValue.toDouble()
    }

    fun setHorizontalAngleSlider(sliderValue: Float) {
        viewShed.horizontalAngle = sliderValue.toDouble()
    }

    fun setPitch(sliderValue: Float) {
        viewShed.pitch = sliderValue.toDouble()
    }

    fun frustumVisibility(checkedValue: Boolean) {
        viewShed.frustumOutlineVisible = checkedValue
    }

    fun analysisVisibility(checkedValue: Boolean) {
        viewShed.isVisible = checkedValue
    }
}

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