Display a line of sight

Learn how to perform and display a line of sight analysis in a 3D scene.

display a line of sight

A line of sight analysis is a type of visibility analysis that calculates visibility (visible or obstructed) between a designated observer and target. It calculates whether the target is visible to the observer based on the environment and renders a line indicating visibility on the map.

In this tutorial, you will perform and display a line of sight analysis in a web scene. Your line of sight analysis will show which targets (hot spots) are visible, based on the terrain, from specified observation points in the Yosemite Valley.

Prerequisites

The following are required for this tutorial:

  1. An ArcGIS account to access your API keys. If you don't have an account, sign up for free.
  2. Your system meets the system requirements.
  3. The ArcGIS Runtime API for iOS is installed.

Steps

Open an Xcode project

This tutorial uses the completed Display a web scene tutorial as a starting point. You can first complete that tutorial or download and unzip the completed solution.

  1. To start this tutorial, first complete the Display a web scene tutorial or download and unzip the solution.
  2. Open the .xcodeproj file in Xcode.

Set the API Key

An API Key enables access to services, web maps, and web scenes hosted in ArcGIS Online.

  1. Go to your developer dashboard to get your API key. For these tutorials, use your default API key. It is scoped to include all of the services demonstrated in the tutorials.

  2. In Xcode, in the Project Navigator, click AppDelegate.swift.

  3. In the editor, set the apiKey property on the AGSArcGISRuntimeEnvironment with your API key.


    AppDelegate.swift
    Change line
                                       
    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
    // Copyright 2021 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
    //
    //   https://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.
    
    
    import UIKit
    
    import ArcGIS
    
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
        var window: UIWindow?
    
        func application(_ application: UIApplication,
                         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            // Note: it is not best practice to store API keys in source code.
            // The API key is referenced here for the convenience of this tutorial.
    
            AGSArcGISRuntimeEnvironment.apiKey = "YOUR_API_KEY"
    
            return true
        }
    
    }
    

Get the web scene item ID

You can use ArcGIS tools to create and view web scenes. Use the Scene Viewer to identify the web scene item ID. This item ID will be used later in the tutorial.

  1. Go to the Yosemite Valley Hotspots web scene in the Scene Viewer in ArcGIS Online. This web scene displays terrain and hotspots in the Yosemite Valley.
  2. Make a note of the item ID at the end of the browser's URL.

    The item ID should be 7558ee942b2547019f66885c44d4f0b1

Update the scene

  1. In Xcode, in the Project Navigator, click ViewController.swift.

  2. In the editor, modify the setupScene() method to create an AGSScene for the web scene. To do this, create a portal item providing the web scene's item ID and an AGSPortal referencing ArcGIS Online.

    ViewController.swift
    Change line
    33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 32 31 30 29 28 27 27 27 27 27 28 29 30 31 32 33 34 35 36 37 37 37 38 38 38 38 37 36 35 34 33 32 31 30 29 28 28 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0 0 -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 -39 -39 -39 -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
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    

Add a UI to control the line of sight analyses

To control the line of sight analyses, some UI is required.

  1. Define a private method named setupUI(). Create a "Clear" button and assign it to the navigation bar. The clear button will remove the line of sight analyses from the scene.

    You set an action on the clear button using a selector for a method that will be added in a later step.

    ViewController.swift
    Add line.Add line.Add line.Add line.
    49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 48 47 46 45 44 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 42 41 41 41 41 41 42 43 44 45 46 46 46 46 46 45 45 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 17 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -22 -22 -22 -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
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    
  2. Define a method named clearScene(_:) and assign it the @objc keyword. This method removes the line of sight analyses from the scene view.

    The @objc method keyword exposes the method to Objective-C, a necessary step for using the UIBarButtonItem API.

    ViewController.swift
    Add line.Add line.Add line.Add line.
    49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 48 47 46 45 44 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 42 41 41 41 41 41 42 43 44 45 46 47 48 49 50 50 50 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 22 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -17 -17 -17 -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
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    
  3. In viewDidLoad(), call setupUI().

    ViewController.swift
    Add line.
    19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 20 21 22 23 24 25 25 25 25 25 26 26 26 26 26 26 26 26 26 26 26 26 26 26 25 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -4 -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 -43 -43 -43 -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
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    
  4. In Xcode, in the Project Navigator, click Main.storyboard.

  5. In the editor, select ViewController. In the menu bar, click Editor > Embed In > Navigation Controller.

    Embedding ViewController within a Navigation Controller places a navigation bar at the top of ViewController. The navigation bar contains the UI elements.

  6. To verify that your UI is set up properly, press <Command+R> to run the app. You should see a "Clear" button contained by the navigation bar and the app should load the Yosemite Valley Hotspots web scene.

Add observer and target graphics

Your app will use graphics to depict the locations of the analyses observer and targets.

  1. In Xcode, in the Project navigator, click ViewController.swift.

  2. Create a private AGSGraphicsOverlay named graphicsOverlay to display the observer and targets graphics.

    A graphics overlay is a container for graphics. It is used with a scene view to display graphics on a scene. You can add more than one graphics overlay to a scene view. Graphics overlays are displayed on top of all the other layers.

    ViewController.swift
    Add line.
    61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 60 59 58 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 56 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 56 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 56 56 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 17 17 17 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    
  3. Create a private AGSGraphic named observerGraphic with a black fill color and white outline. This graphic is used to depict the location of the line of sight analyses observer and is initialized with a nil geometry because the observer's location is not yet determined.

    A graphic with nil geometry will not be rendered in the scene view, even if it has been added to a graphics overlay.

    ViewController.swift
    Add line.Add line.Add line.Add line.Add line.
    61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 60 59 58 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 56 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 56 57 58 59 60 61 62 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 42 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 3 3 3 3 2 1 0 -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
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    
  4. Create a private method named addTargetGraphic(point:) that accepts an AGSPoint as a parameter. This method is used to create graphics with a white fill color and black outline to depict the locations of line of sight analyses targets.

    ViewController.swift
    Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.
    63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 62 61 60 59 59 59 59 59 59 59 59 59 59 59 59 59 59 59 58 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 71 70 69 68 67 66 65 64 63 62 61 60 60 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 21 21 21 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    
  5. Create a private computed variable named targetGraphics to filter and return all graphics contained by graphicsOverlay that depict target locations. The graphics overlay contains one observerGraphic and zero or several target graphics so any graphic that is not the observer is a target.

    ViewController.swift
    Add line.Add line.Add line.
    69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 68 67 66 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 64 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 64 65 66 67 68 69 70 71 72 73 74 75 75 74 73 72 71 70 69 68 68 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 29 29 29 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    
  6. Define a private method named setupGraphicsOverlay() to specify various sceneProperties of the graphicsOverlay and add it to the scene view. Scene properties of the graphics overlay allow you to control 3D-specific rendering of graphics.

    • Specifying the surfacePlacement as absolute treats graphics geometry z-values as absolute altitude values. If the geometry does not contain a z-value or has a value of 0, the graphic will be rendered at surface level. Increasing the z-value renders the graphic at a higher altitude.
    • Specifying the altitudeOffset increases the altitude by adding the specified value to the z-value altitude.
    ViewController.swift
    Add line.Add line.Add line.Add line.Add line.Add line.
    78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 77 76 75 74 74 74 74 74 74 74 74 74 74 74 74 74 74 74 73 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 73 74 75 76 77 78 79 80 81 82 82 82 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 43 43 43 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    
  7. In viewDidLoad(), call the method setupGraphicsOverlay().

    ViewController.swift
    Add line.
    19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 20 21 22 23 24 25 26 27 27 27 28 28 28 28 28 28 28 28 28 28 28 28 28 28 27 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -13 -13 -13 -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
    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
    // Copyright 2021 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.
    
    import ArcGIS
    
    class ViewController: UIViewController {
        @IBOutlet weak var sceneView: AGSSceneView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScene()
    
            setupUI()
    
            setupGraphicsOverlay()
    
            setupAnalysisOverlay()
    
        }
    
        // MARK: Scene
    
        private func setupScene() {
            let scene: AGSScene = {
                let portal = AGSPortal.arcGISOnline(withLoginRequired: false)
    
                let item = AGSPortalItem(portal: portal, itemID: "7558ee942b2547019f66885c44d4f0b1")
    
                return AGSScene(item: item)
            }()
            sceneView.scene = scene
    
            sceneView.touchDelegate = self
    
        }
    
        // MARK: UI
    
        private func setupUI() {
            let clear = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearScene))
            navigationItem.rightBarButtonItem = clear
        }
    
        @objc
        func clearScene(_ sender: UIBarButtonItem) {
            clearAnalyses()
        }
    
        // MARK: Graphics
    
        private let graphicsOverlay = AGSGraphicsOverlay()
    
        private let observerGraphic: AGSGraphic = {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .black, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .white, width: 2)
            return AGSGraphic(geometry: nil, symbol: symbol)
        }()
    
        @discardableResult
        private func addTargetGraphic(point: AGSPoint) -> AGSGraphic {
            let symbol = AGSSimpleMarkerSymbol(style: .circle, color: .white, size: 16)
            symbol.outline = AGSSimpleLineSymbol(style: .solid, color: .black, width: 2)
            let targetGraphic = AGSGraphic(geometry: point, symbol: symbol, attributes: nil)
            graphicsOverlay.graphics.add(targetGraphic)
            return targetGraphic
        }
    
        private var targetGraphics: [AGSGraphic] {
            graphicsOverlay.graphics.filter { ($0 as! AGSGraphic) != observerGraphic } as! [AGSGraphic]
        }
    
        private func setupGraphicsOverlay() {
            graphicsOverlay.graphics.add(observerGraphic)
            graphicsOverlay.sceneProperties?.surfacePlacement = .absolute
            graphicsOverlay.sceneProperties?.altitudeOffset = 5
            sceneView.graphicsOverlays.add(graphicsOverlay)
        }
    
        // MARK: Line of Sight Analysis
    
        private let analysisOverlay = AGSAnalysisOverlay()
    
        private func setupAnalysisOverlay() {
            sceneView.analysisOverlays.add(analysisOverlay)
        }
    
        private func createLineOfSightAnalysis(observer: AGSGeoElement, target: AGSGeoElement) {
            let line = AGSGeoElementLineOfSight(observerGeoElement: observer, targetGeoElement: target)
            analysisOverlay.analyses.add(line)
        }
    
        private func clearAnalyses() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
            graphicsOverlay.graphics.removeObjects(in: targetGraphics)
        }
    
        private func setObserver(point: AGSPoint) {
            let shouldCreateAnalyses = observerGraphic.geometry == nil
            observerGraphic.geometry = point
            if shouldCreateAnalyses {
                targetGraphics.forEach { targetGraphic in
                    createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
                }
            }
        }
    
        private func clearObserver() {
            analysisOverlay.analyses.removeAllObjects()
            observerGraphic.geometry = nil
        }
    
        private func addTarget(point: AGSPoint) {
            let targetGraphic = addTargetGraphic(point: point)
            if observerGraphic.geometry != nil {
                createLineOfSightAnalysis(observer: observerGraphic, target: targetGraphic)
            }
        }
    
    }
    
    // MARK: - GeoView Touch Delegate
    
    extension ViewController: AGSGeoViewTouchDelegate {
    
        func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            addTarget(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didMoveLongPressToScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoView(_ geoView: AGSGeoView, didEndLongPressAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
    
            setObserver(point: mapPoint)
    
        }
    
        func geoViewDidCancelLongPress(_ geoView: AGSGeoView) {
    
            clearObserver()
    
        }
    
    }
    

Create line of sight analyses

Visual analyses help you make sense of complex 3D data contained by a scene. Use an AGSGeoElementLineOfSight to perform and display a line of sight analysis using 3D geoelements to define observer and target locations.

  1. Create a private AGSAnalysisOverlay named analysisOverlay to contain and display the line of sight analyses.

    An analysis overlay is a container for analyses. It is used with a scene view to display visual analyses on a scene. You can add more than one analysis overlay to a scene view. Analysis overlays are displayed on top of all other layers and graphics overlays.

    ViewController.swift
    Add line.
    91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 91 90 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 88 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 88 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 88 88 88 88 88 87 86 85 84 83 82 81