Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS Runtime SDK for Qt

Viewshed (location)

Sample Viewer View Sample on GitHub

This sample demonstrates how to calculate a viewshed from a Point 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. To move the viewshed, double touch and drag your finger across the screen. Lift your finger to stop moving the viewshed.

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.

Tags

3D, frustum, LocationViewshed, Scene, viewshed, visibility analysis

Sample Code

import QtQuick 2.6
import QtQuick.Controls 2.2
import Esri.ArcGISExtras 1.1
import Esri.ArcGISRuntime 100.9

Rectangle {
    id: viewshedSample
    clip: true
    width: 800
    height: 600
    
    property bool calculating: false

    SceneView {
        id: sceneView
        anchors.fill: parent
        focus: true

        Scene {
            id: scene
            BasemapTopographic {}

            Surface {
                ArcGISTiledElevationSource {
                    url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
                }
            }

            onLoadStatusChanged: {
                if (loadStatus === Enums.LoadStatusLoaded) {
                    // Set a viewpoint
                    const point = ArcGISRuntimeEnvironment.createObject("Point", {
                                                                            x: 6.86088,
                                                                            y: 45.3604,
                                                                            z: 3582.55,
                                                                            spatialReference: Factory.SpatialReference.createWgs84()
                                                                        });

                    const camera = ArcGISRuntimeEnvironment.createObject("Camera", {
                                                                             location: point,
                                                                             heading: 345,
                                                                             pitch: 70,
                                                                             roll: 0
                                                                         });

                    sceneView.setViewpointCamera(camera)
                }
            }
        }

        // Add an Analysis Overlay
        AnalysisOverlay {
            id: analysisOverlay

            LocationViewshed {
                id: locationViewshed
                minDistance: 50
                maxDistance: 1000
                horizontalAngle: 45
                verticalAngle: 90
                heading: 180
                pitch: 90
                visible: true
            }
        }

        onMouseClicked: locationViewshed.location = sceneView.screenToBaseSurface(mouse.x, mouse.y);
        onMousePressedAndHeld: calculating = true;
        onMouseReleased: calculating = false;
        onMousePositionChanged: locationViewshed.location = sceneView.screenToBaseSurface(mouse.x, mouse.y);

        Rectangle {
            anchors {
                right: parent.right
                bottom: sceneView.attributionTop
                margins: 10
            }
            visible: !optionPanel.visible
            width: 45
            height: width
            color: "white"
            radius: 25

            Image {
                anchors.centerIn: parent
                source: "qrc:/Samples/Analysis/ViewshedLocation/settings.png"
                width: 40
                height: width
            }

            MouseArea {
                anchors.fill: parent
                onClicked: optionPanel.visible = true;
            }
        }


        Rectangle {
            id: optionPanel
            anchors {
                right: parent.right
                top: parent.top
                bottom: sceneView.attributionTop
            }
            width: 260
            visible: false
            color: "white"
            opacity: 0.85

            Flickable {
                anchors {
                    fill: parent
                    margins: 5
                }
                contentWidth: parent.width
                contentHeight: parent.height
                flickableDirection: Flickable.VerticalFlick

                Column {
                    id: optionColumn
                    spacing: 10
                    width: optionPanel.width

                    Item {
                        width: parent.width
                        height: 25

                        Text {
                            text: "Viewshed Options"
                            anchors.horizontalCenter: parent.horizontalCenter
                            font.pixelSize: 18
                            font.underline: true

                        }

                        Rectangle {
                            anchors {
                                right: parent.right
                                margins: 10
                                verticalCenter: parent.verticalCenter
                            }
                            width: 45
                            height: width
                            color: "transparent"

                            Image {
                                anchors.centerIn: parent
                                source: "qrc:/Samples/Analysis/ViewshedLocation/close.png"
                                width: 40
                                height: width
                            }

                            MouseArea {
                                anchors.fill: parent
                                onClicked: optionPanel.visible = false;
                            }
                        }
                    }

                    Item {
                        width: parent.width
                        height: 25

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            width: parent.width * 0.75
                            text: qsTr("Viewshed Visible")
                            font.pixelSize: 14
                        }

                        Switch {
                            anchors {
                                right: parent.right
                                margins: 10
                                verticalCenter: parent.verticalCenter
                            }
                            checked: true
                            onCheckedChanged: locationViewshed.visible = checked;
                        }
                    }

                    Item {
                        width: parent.width
                        height: 25

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            width: parent.width * 0.75
                            text: qsTr("Frustum Outline Visible")
                            font.pixelSize: 14
                        }

                        Switch {
                            anchors {
                                right: parent.right
                                margins: 10
                                verticalCenter: parent.verticalCenter
                            }
                            checked: locationViewshed.frustumOutlineVisible
                            onCheckedChanged: locationViewshed.frustumOutlineVisible = checked;
                        }
                    }

                    ViewshedSlider {
                        titleText: qsTr("Min Distance (m)")
                        parameterValue: locationViewshed.minDistance
                        minValue: 1
                        maxValue: 2000
                        onParameterValueChanged: locationViewshed.minDistance = parameterValue;
                    }

                    ViewshedSlider {
                        titleText: qsTr("Max Distance (m)")
                        parameterValue: locationViewshed.maxDistance
                        minValue: 1
                        maxValue: 2000
                        onParameterValueChanged: locationViewshed.maxDistance = parameterValue;
                    }

                    ViewshedSlider {
                        titleText: qsTr("Vertical Angle")
                        parameterValue: locationViewshed.verticalAngle
                        minValue: 1
                        maxValue: 120
                        onParameterValueChanged: locationViewshed.verticalAngle = parameterValue;
                    }

                    ViewshedSlider {
                        titleText: qsTr("Horizontal Angle")
                        parameterValue: locationViewshed.horizontalAngle
                        minValue: 1
                        maxValue: 120
                        onParameterValueChanged: locationViewshed.horizontalAngle = parameterValue;
                    }

                    ViewshedSlider {
                        titleText: qsTr("Heading")
                        parameterValue: locationViewshed.heading
                        minValue: 1
                        maxValue: 359
                        onParameterValueChanged: locationViewshed.heading = parameterValue;
                    }

                    ViewshedSlider {
                        titleText: qsTr("Pitch")
                        parameterValue: locationViewshed.pitch
                        minValue: 1
                        maxValue: 179
                        onParameterValueChanged: locationViewshed.pitch = parameterValue;
                    }

                    Row {
                        width: parent.width
                        height: 25
                        spacing: 5

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            width: parent.width * 0.85
                            text: qsTr("Visible Color")
                            font.pixelSize: 14
                        }

                        Rectangle {
                            id: visibleColorRect
                            anchors {
                                margins: 10
                                verticalCenter: parent.verticalCenter
                            }
                            width: 25
                            height: width
                            border {
                                color: "black"
                                width: 1
                            }
                            color: Viewshed.visibleColor()
                            radius: 4

                            MouseArea {
                                anchors.fill: parent
                                onClicked: {
                                    visibleColorDialog.open();
                                }
                            }
                        }
                    }

                    Row {
                        width: parent.width
                        height: 25
                        spacing: 5

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            width: parent.width * 0.85
                            text: qsTr("Obstructed Color")
                            font.pixelSize: 14
                        }

                        Rectangle {
                            id: obstructedColorRect
                            anchors {
                                margins: 10
                                verticalCenter: parent.verticalCenter
                            }
                            width: 25
                            height: width
                            border {
                                color: "black"
                                width: 1
                            }
                            color: Viewshed.obstructedColor()
                            radius: 4

                            MouseArea {
                                anchors.fill: parent
                                onClicked: {
                                    obstructedColorDialog.open();
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    ColorDialog {
        id: visibleColorDialog
        modal: true
        x: Math.round(parent.width - width) / 2
        y: Math.round(parent.height - height) / 2
        onAccepted: {
            visibleColorRect.color = color;
            Viewshed.setVisibleColor(color);
        }
        onOpened: {
            setColor(visibleColorRect.color);
        }
    }

    ColorDialog {
        id: obstructedColorDialog
        modal: true
        x: Math.round(parent.width - width) / 2
        y: Math.round(parent.height - height) / 2
        onAccepted: {
            obstructedColorRect.color = color;
            Viewshed.setObstructedColor(color);
        }
        onOpened: {
            setColor(obstructedColorRect.color);
        }
    }
}
import QtQuick 2.6
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3

Dialog {
    id: colorDialog
    property alias red: redSlider.value
    property alias green: greenSlider.value
    property alias blue: blueSlider.value
    property alias alpha: alphaSlider.value
    readonly property color color: Qt.rgba(red, green, blue, alpha);

    title: "Pick a color"
    standardButtons: Dialog.Ok | Dialog.Cancel

    function setColor(c) {
        red = c.r;
        green = c.g;
        blue = c.b;
        alpha = c.a;
    }

    GridLayout {
        columns: 2
        Rectangle {
            Layout.columnSpan: 2
            Layout.fillWidth: true
            height: redSlider.height
            border {
                color: "black"
                width: 1
            }
            radius: 2
            color: colorDialog.color
        }

        Text {
            text: "Red"
        }

        Slider {
            id: redSlider
        }

        Text {
            text: "Green"
        }

        Slider {
            id: greenSlider
        }

        Text {
            text: "Blue"
        }

        Slider {
            id: blueSlider
        }

        Text {
            text: "Alpha"
        }

        Slider {
            id: alphaSlider
        }
    }
}
import QtQuick 2.6
import QtQuick.Controls 2.2

Column {
    id: root
    width: parent.width
    spacing: 5

    property string titleText
    property real parameterValue
    property real minValue: 1
    property real maxValue: 179

    Text {
        width: 80
        text: titleText
        font.pixelSize: 14
    }

    Row {
        width: parent.width
        height: 25
        spacing: 5

        Slider {
            anchors.verticalCenter: parent.verticalCenter
            orientation: Qt.Horizontal
            from: minValue
            to: maxValue
            width: parent.width * 0.75
            value: parameterValue

            onValueChanged: {
                parameterValue = value;
            }
        }

        Text {
            anchors.verticalCenter: parent.verticalCenter
            horizontalAlignment: Text.AlignRight
            text: Math.round(parameterValue)
            font.pixelSize: 14
        }
    }
}