Learn how to perform and display a line of sight analysis in a 3D scene.
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:
- An ArcGIS account to access your API keys. If you don't have an account, sign up for free.
- Your system meets the system requirements.
Steps
Open the Xcode project
-
To start this tutorial, first complete the Display a web scene tutorial or download and unzip the solution.
-
Open the
.xcodeproj
file in Xcode. -
If you downloaded the solution project, set your API key.
An API Key enables access to services, web maps, and web scenes hosted in ArcGIS Online.
-
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.
- In Xcode, in the Project Navigator, click MainApp.swift.
- In the Editor, set the
ArcGISEnvironment.apiKey
property on theArcGISEnvironment
with your API key.
MainApp.swiftUse dark colors for code blocks import SwiftUI import ArcGIS @main struct MainApp: App { init() { ArcGISEnvironment.apiKey = APIKey("<#your-API-key#>") } var body: some SwiftUI.Scene { WindowGroup { ContentView() .ignoresSafeArea() } } }
-
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.
- 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.
- Make a note of the item ID at the end of the browser's URL. The item ID should be 7558ee942b2547019f66885c44d4f0b1
Update the scene
-
In Xcode, in the Project Navigator, click ContentView.swift.
-
In the editor, modify the
scene
variable to create aScene
for the web scene. To do this, create a portal item providing the web scene's item ID and aPortal
referencing ArcGIS Online.ContentView.swiftUse dark colors for code blocks Change line Change line Change line Change line // The Yosemite Valley hotspots scene. @State private var scene: ArcGIS.Scene = { let portalItem = PortalItem( portal: .arcGISOnline(connection: .anonymous), id: Item.ID("7558ee942b2547019f66885c44d4f0b1")! ) return Scene(item: portalItem) }()
Set observer and target symbols
Your app will use graphics to depict the locations of the analyses observer and targets.
-
Create a private class named
Model
of typeObservable
. See the programming patterns page for more information on how to manage states.Object -
Create a
GraphicsOverlay
namedgraphics
and set some of its properties to display the observer and targets graphics.Overlay 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.
ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. private class Model: ObservableObject { // MARK: Graphics // The graphics overlay to add graphics to. let graphicsOverlay: GraphicsOverlay = { let graphicsOverlay = GraphicsOverlay() graphicsOverlay.sceneProperties.surfacePlacement = .absolute graphicsOverlay.sceneProperties.altitudeOffset = 5 return graphicsOverlay }() }
-
Create a private
Symbol
namedobserver
with a black fill color and white outline. Create another private symbol namedSymbol target
with a white fill color and black outline. These symbols are used to depict the location of the line of sight analyses observer and target respectively. Both symbols will be used to make graphics when the locations are determined.Symbol ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private class Model: ObservableObject { // MARK: Graphics // The graphics overlay to add graphics to. let graphicsOverlay: GraphicsOverlay = { let graphicsOverlay = GraphicsOverlay() graphicsOverlay.sceneProperties.surfacePlacement = .absolute graphicsOverlay.sceneProperties.altitudeOffset = 5 return graphicsOverlay }() // The symbol to indicate the observer. private let observerSymbol: Symbol = { let symbol = SimpleMarkerSymbol( style: .circle, color: .black, size: 20 ) symbol.outline = SimpleLineSymbol( style: .solid, color: .white, width: 2 ) return symbol }() // The symbol to indicate the target. private var targetSymbol: Symbol = { let symbol = SimpleMarkerSymbol( style: .circle, color: .white, size: 20 ) symbol.outline = SimpleLineSymbol( style: .solid, color: .black, width: 2 ) return symbol }() }
Create line of sight analyses
Visual analyses help you make sense of complex 3D data contained by a scene. Use a LocationLineOfSight
to perform and display a line of sight analysis using observer and target locations.
-
Create an
AnalysisOverlay
namedanalysis
to contain and display the line of sight analyses.Overlay An analysis overlay is a container for
Analysis
objects. 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.ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. // MARK: Analysis // The analysis overlay to be added to the scene. let analysisOverlay = AnalysisOverlay()
-
Create a private variable named
observer
of typeLocation Point
.ContentView.swiftUse dark colors for code blocks Add line. Add line. // MARK: Analysis // The analysis overlay to be added to the scene. let analysisOverlay = AnalysisOverlay() // The location of the observer. private var observerLocation: Point?
-
Define a private method named
create
that accepts aLine Of Sight(scene Point: ) Point
as a parameter. This method creates aLocationLineOfSight
analysis, adds it to the scene view analysis overlay, and adds the observer and target graphics to the graphics overlay.A
LocationLineOfSight
is used to perform and display a line of sight analysis between twoPoint
types, like a feature or graphic. Alternatively, you can perform and display a line of sight analysis between twoGeoElement
or graphic using aGeo
.Element Line Of Sight ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. // MARK: Analysis // The analysis overlay to be added to the scene. let analysisOverlay = AnalysisOverlay() // The location of the observer. private var observerLocation: Point? // Creates the line of sight with the scene point. func createLineOfSight(scenePoint: Point) { }
-
If a line of sight analysis already exists, create a new line of sight analysis using the existing
observer
and theLocation point
. Add the new analysis to the analysis overlay and create and add a target graphic.ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. // MARK: Analysis // The analysis overlay to be added to the scene. let analysisOverlay = AnalysisOverlay() // The location of the observer. private var observerLocation: Point? // Creates the line of sight with the scene point. func createLineOfSight(scenePoint: Point) { if let observerLocation { // Creates and set initial line of sight analysis with the tapped scene point. let lineOfSight = LocationLineOfSight(observerLocation: observerLocation, targetLocation: scenePoint) // Adds the line of sight to the analysis overlay. analysisOverlay.addAnalysis(lineOfSight) // Creates a target graphic and adds it to the graphics overlay. let targetGraphic = Graphic(geometry: scenePoint, symbol: targetSymbol) graphicsOverlay.addGraphic(targetGraphic) } }
-
If creating a new line of sight analysis, create a new
Location
with theLine Of Sight point
as the observer and target location. The target location will get a new location upon a secondary tap by the user. Add the new analysis to the analysis overlay withadd
. Lastly, create a graphic for the observer using the observer symbol previously created.Analysis(_ : ) ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. // Creates the line of sight with the scene point. func createLineOfSight(scenePoint: Point) { if let observerLocation { // Creates and set initial line of sight analysis with the tapped scene point. let lineOfSight = LocationLineOfSight(observerLocation: observerLocation, targetLocation: scenePoint) // Adds the line of sight to the analysis overlay. analysisOverlay.addAnalysis(lineOfSight) // Creates a target graphic and adds it to the graphics overlay. let targetGraphic = Graphic(geometry: scenePoint, symbol: targetSymbol) graphicsOverlay.addGraphic(targetGraphic) } else { // Updates the observer location. observerLocation = scenePoint // Adds the observer graphic to the graphics overlay. let observerGraphic = Graphic(geometry: scenePoint, symbol: observerSymbol) graphicsOverlay.addGraphic(observerGraphic) } }
-
Define a private method named
clear
to remove all line of sight analyses from the scene view, remove all graphics from the graphics overlay, and set theAnalyses() line
variable toOf Sight nil
.ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. else { // Updates the observer location. observerLocation = scenePoint // Adds the observer graphic to the graphics overlay. let observerGraphic = Graphic(geometry: scenePoint, symbol: observerSymbol) graphicsOverlay.addGraphic(observerGraphic) } } // Resets the observer location and clears all graphics and analyses. func clearAnalyses() { analysisOverlay.removeAllAnalyses() graphicsOverlay.removeAllGraphics() observerLocation = nil }
Display the line of sight analyses with touch events
Touch events determine where to place the observer and targets as well as display line of sight analyses. A user taps to place targets and then long-press and drags to place the observer and display line of sight analyses.
-
The first step to displaying the analyses and graphics is to add the analysis and graphics overlays to the scene view. In the
Content
struct, create a variable of typeView Model
with a@State
property wrapper and add the overlays to the scene view.Object ContentView.swiftUse dark colors for code blocks Add line. Add line. Change line struct ContentView: View { // An ObservableObject containing the scene, graphics overlay, and analysis overlay. @StateObject private var model = Model() // The Yosemite Valley hotspots scene. @State private var scene: ArcGIS.Scene = { let portalItem = PortalItem( portal: .arcGISOnline(connection: .anonymous), id: Item.ID("7558ee942b2547019f66885c44d4f0b1")! ) return Scene(item: portalItem) }() var body: some View { SceneView(scene: scene, graphicsOverlays: [model.graphicsOverlay], analysisOverlays: [model.analysisOverlay]) } }
-
To add a new line of sight with each tap, add the
o
method to then Single T a p Gesture(perform: ) Scene
. In the closure, call the previously createdView Create
method, passing in theObserver Location(scene Point: ) scene
.Point ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. var body: some View { SceneView(scene: scene, graphicsOverlays: [model.graphicsOverlay], analysisOverlays: [model.analysisOverlay]) .onSingleTapGesture { _, scenePoint in // Places an observer point upon single touch. guard let scenePoint else { return } model.createLineOfSight(scenePoint: scenePoint) } }
Add a UI to control the line of sight analyses
To control the line of sight analyses, some UI is required.
-
Lastly, create a toolbar and add a "Clear" button to reset the analysis and graphics overlays. The "Clear" button calls the previously created
clear
method. This will allow users a fresh start to make more analyses.Analyses() ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. var body: some View { SceneView(scene: scene, graphicsOverlays: [model.graphicsOverlay], analysisOverlays: [model.analysisOverlay]) .onSingleTapGesture { _, scenePoint in // Places an observer point upon single touch. guard let scenePoint else { return } model.createLineOfSight(scenePoint: scenePoint) } .toolbar { ToolbarItemGroup(placement: .bottomBar) { Button("Clear") { // Resets the line of sight. model.clearAnalyses() } } } }
Run the app
Press Command + R to run the app.
If you are using the Xcode simulator your system must meet these minimum requirements: macOS Monterey 12.5, Xcode 15, iOS 17. If you are using a physical device, then refer to the system requirements.
You should see a scene of hotspots in the Yosemite Valley. Long-press and drag to set the location of the observer and tap to add target locations. The application performs and displays line of sight analyses between the observer and its targets.