Learn how to evaluate the horizontal, vertical, and direct distances between two points in a 3D Scene.

A distance measurement analysis is a type of measurement analysis that calculates and displays the distance between start point and end point locations. The analysis evaluates the vertical, horizontal, and direct distances between the two 3D points and renders a measurement visualization on-screen.
In this tutorial you will perform and display a distance measurement analysis in a web scene. Using the distance measurement analysis you will measure distances between hotspots in the Yosemite Valley.
Prerequisites
Before starting this tutorial:
-
You need an ArcGIS Location Platform or ArcGIS Online account.
-
Your system meets the system requirements.
Develop or Download
You have two options for completing this tutorial:
Option 1: Develop the code
To start the tutorial, complete the Display a web scene tutorial. This creates a scene to display the trails, trailheads, and parks in the Santa Monica Mountains.
Continue with the following instructions to evaluate the horizontal, vertical, and direct distances between two points in a 3D Scene.
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 58 59 60 65 66 67Change 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) }()
Create a distance measurement analysis
Use a LocationDistanceMeasurement
to perform and display a distance measurement analysis using 3D points to define the start and end locations.
-
Create a private class named
Model
of typeObservable
and an accompanying State Object reference in the ContentView struct. See the programming patterns page for more information on how to manage states.Object ContentView.swiftUse dark colors for code blocks 21 22 23 26Add line. Add line. Add line. Add line. Add line. private class Model: ObservableObject { } struct ContentView: View { // An ObservableObject containing the scene, and analysis overlay. @StateObject private var model = Model()
-
Create an
AnalysisOverlay
namedanalysis
to contain and display the distance measurement analyses.Overlay An analysis overlay is a container for
Analysis
objects, such as theLocationDistanceMeasurement
. It is used to render visual analyses on a scene in a scene view. 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 18 19 22 23Add line. Add line. private class Model: ObservableObject { // The analysis overlay to be added to the scene. let analysisOverlay = AnalysisOverlay() }
-
Create a
LocationDistanceMeasurement
namedmeasurement
. Upon launch, the distance measurement visualization should not be visible.ContentView.swiftUse dark colors for code blocks 18 19 20 21 22 30 31Add line. Add line. Add line. Add line. Add line. Add line. Add line. private class Model: ObservableObject { // The analysis overlay to be added to the scene. let analysisOverlay = AnalysisOverlay() let measurement: LocationDistanceMeasurement = { let point = Point(x: -119.5968, y: 37.7567, z: 58.5011, spatialReference: .wgs84) let measurement = LocationDistanceMeasurement(startLocation: point, endLocation: point) measurement.isVisible = false measurement.unitSystem = .metric return measurement }() }
-
In
init()
, add the distance measurement to the analysis overlay.The distance measurement analysis is added to a scene view using an analysis overlay. An analysis overlay is a container for analyses. It can be used to display visual analyses in a scene view. You can add more than one analysis overlay and they are displayed on top of all other layers.
ContentView.swiftUse dark colors for code blocks 18 19 20 21 22 23 24 25 26 27 28 29 30 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51Add line. Add line. Add line. private class Model: ObservableObject { // The analysis overlay to be added to the scene. let analysisOverlay = AnalysisOverlay() let measurement: LocationDistanceMeasurement = { let point = Point(x: -119.5968, y: 37.7567, z: 58.5011, spatialReference: .wgs84) let measurement = LocationDistanceMeasurement(startLocation: point, endLocation: point) measurement.isVisible = false measurement.unitSystem = .metric return measurement }() init() { analysisOverlay.addAnalysis(measurement) } func moveDistanceMeasurement(point: Point) { if measurement.startLocation == measurement.endLocation { measurement.startLocation = point } else { measurement.endLocation = point } measurement.isVisible = true } func clearMeasurement() { measurement.isVisible = false let point = Point(x: 0, y: 0, z: 0, m: 0, spatialReference: .wgs84) measurement.startLocation = point measurement.endLocation = point } }
-
Define a method named
move
that receives a point as a parameter. This method is used to set the distance measurement analysis and make it visible, if it's not visible already.Distance Measurement(point :) ContentView.swiftUse dark colors for code blocks 43Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. func moveDistanceMeasurement(point: Point) { if measurement.startLocation == measurement.endLocation { measurement.startLocation = point } else { measurement.endLocation = point } measurement.isVisible = true }
-
Define a private method named
clear
. This method is used to hide and clear the distance measurement analysis. Set the measurement's visibility toMeasurement() false
and reset the start and end locations to allow new measurements.ContentView.swiftUse dark colors for code blocks 35 36 37 38 39 40 41 42 43Add line. Add line. Add line. Add line. Add line. Add line. func moveDistanceMeasurement(point: Point) { if measurement.startLocation == measurement.endLocation { measurement.startLocation = point } else { measurement.endLocation = point } measurement.isVisible = true } func clearMeasurement() { measurement.isVisible = false let point = Point(x: 0, y: 0, z: 0, m: 0, spatialReference: .wgs84) measurement.startLocation = point measurement.endLocation = point }
Display the distance measurement analysis with touch events
Touch events determine where to perform and display the distance measurement analysis. A user can tap to add start and end points or long-press and drag to move the distance measurement analysis' location around the screen.
-
The first step to displaying the analyses is to add the analysis to the scene view. Modify the scene view to add the analysis overlay.
ContentView.swiftUse dark colors for code blocks 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 73 74 75 76 77Change line struct ContentView: View { // An ObservableObject containing the scene, 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 { VStack { SceneView(scene: scene, analysisOverlays: [model.analysisOverlay]) } } }
-
When a user performs a single touch or a long-press touch event, call
move
.Distance Measurement(point :) ContentView.swiftUse dark colors for code blocks 69 70 71 72 73 82 83 84Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. var body: some View { VStack { SceneView(scene: scene, analysisOverlays: [model.analysisOverlay]) .onSingleTapGesture { _, scenePoint in guard let scenePoint else { return } model.moveDistanceMeasurement(point: scenePoint) } .onLongPressGesture { _, scenePoint in guard let scenePoint else { return } model.moveDistanceMeasurement(point: scenePoint) } } }
Add a UI to control the distance measurement analysis
To control the distance measurement analysis, some UI is required.
-
Add a toolbar and create a "Clear" button. The button hides and clears the distance measurement analysis from the scene. When the button is selected, call the
Clear
method.Measurement ContentView.swiftUse dark colors for code blocks 69 70 71 72 73 74 75 76 77 78 79 80 81 82 91 92 93Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. var body: some View { VStack { SceneView(scene: scene, analysisOverlays: [model.analysisOverlay]) .onSingleTapGesture { _, scenePoint in guard let scenePoint else { return } model.moveDistanceMeasurement(point: scenePoint) } .onLongPressGesture { _, scenePoint in guard let scenePoint else { return } model.moveDistanceMeasurement(point: scenePoint) } .toolbar { ToolbarItemGroup(placement: .bottomBar) { Button("Clear") { // Resets the distance measurement. model.clearMeasurement() } } } } }
Format measurements for readability
Display the distance measurement results in a reader-friendly format.
-
In the
Content
, add threeView @
variables to contain the text measurements.State ContentView.swiftUse dark colors for code blocks 58 59 60 61 62 63 64 65 66 67 68 72 73 74 75 76Add line. Add line. Add 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) }() @State private var directDistanceText = "--" @State private var horizontalDistanceText = "--" @State private var verticalDistanceText = "--" var body: some View { VStack { SceneView(scene: scene, analysisOverlays: [model.analysisOverlay])
-
Create a private extension outside of the
Content
struct namedView Format
. Create a variable namedStyle distance
and set the format style to abbreviated, used as provided, and 2 decimal places.ContentView.swiftUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. private extension FormatStyle where Self == Measurement<UnitLength>.FormatStyle { /// The format style for the distances. static var distance: Self { .measurement(width: .abbreviated, usage: .asProvided, numberFormatStyle: .number.precision(.fractionLength(2))) } }
-
Add a task function to the
Scene
that produces formatted measurements as the values change.View ContentView.swiftUse dark colors for code blocks 76 77 78 79 80 81 82 83 84 85 86Add line. Add line. Add line. Add line. Add line. Add line. Add line. SceneView(scene: scene, analysisOverlays: [model.analysisOverlay]) .onSingleTapGesture { _, scenePoint in guard let scenePoint else { return } model.moveDistanceMeasurement(point: scenePoint) } .onLongPressGesture { _, scenePoint in guard let scenePoint else { return } model.moveDistanceMeasurement(point: scenePoint) } .task { for await measurements in model.measurement.measurements { directDistanceText = measurements.directDistance.formatted(.distance) horizontalDistanceText = measurements.horizontalDistance.formatted(.distance) verticalDistanceText = measurements.verticalDistance.formatted(.distance) } }
-
Add text above the toolbar to display the measurements.
ContentView.swiftUse dark colors for code blocks 99 100 101 102 103 104 105 106 107Add line. Add line. Add line. Add line. Text("Direct: \(directDistanceText)") Text("Horizontal: \(horizontalDistanceText)") Text("Vertical: \(verticalDistanceText)") Spacer() .toolbar { ToolbarItemGroup(placement: .bottomBar) { Button("Clear") { // Resets the distance measurement. model.clearMeasurement() } } }
-
In the Project Navigator, click MainApp.swift. Change the
ignores
method to specify the top edge. This will make the text visible.Safe Area( _:edges :) MainApp.swiftUse dark colors for code blocks 58 59 60 61 63 64 65Change line var body: some SwiftUI.Scene { WindowGroup { ContentView() .ignoresSafeArea(edges: .top) } }
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 14 (Sonoma), Xcode 16, iOS 18. If you are using a physical device, then refer to the system requirements.
You should see a scene of hotspots in the Yosemite Valley. Tap to set start and end locations. Long-press and drag to display and move a distance measurement analysis to evaluate the horizontal, vertical, and direct distances between two park locations.
Alternatively, you can download the tutorial solution, as follows.
Option 2: Download the solution
-
Click the
Download solution
link under Solution and unzip the file to a location on your machine. -
Open the
.xcodeproj
file in Xcode.
Since the downloaded solution does not contain authentication credentials, you must first set up authentication to create credentials, and then add the developer credentials to the solution.
Set up authentication
To access the secure ArcGIS location services used in this tutorial, you must implement API key authentication or user authentication using an ArcGIS Location Platform or an ArcGIS Online account.
You can implement API key authentication or user authentication in this tutorial. Compare the differences below:
API key authentication
- Users are not required to sign in.
- Requires creating an API key credential with the correct privileges.
- API keys are long-lived access tokens.
- Service usage is billed to the API key owner/developer.
- Simplest authentication method to implement.
- Recommended approach for new ArcGIS developers.
Learn more in API key authentication.
User authentication
- Users are required to sign in with an ArcGIS account.
- User accounts must have privilege to access the ArcGIS services used in application.
- Requires creating OAuth credentials.
- Application uses a redirect URL and client ID.
- Service usage is billed to the organization of the user signed into the application.
Learn more in User authentication.
Create a new API key access token with privileges to access the secure resources used in this tutorial.
-
Complete the Create an API key tutorial and create an API key with the following privilege(s):
- Privileges
- Location services > Basemaps
- Privileges
-
Copy and paste the API key access token into a safe location. It will be used in a later step.
Set developer credentials in the solution
To allow your app users to access ArcGIS location services, use the developer credentials that you created in the Set up authentication step to authenticate requests for resources.
Pass your API Key access token to the ArcGISEnvironment
.
-
In the Project Navigator, click MainApp.swift.
-
Set the
Authentication
toMode .api
.Key MainApp.swiftUse dark colors for code blocks // Change the `AuthenticationMode` to `.apiKey` if your application uses API key authentication. private var authenticationMode: AuthenticationMode { .apiKey }
-
Set the
api
property with your API key access token.Key MainApp.swiftUse dark colors for code blocks // Please enter an API key access token if your application uses API key authentication. private let apiKey = APIKey("<#YOUR-ACCESS-TOKEN#>")
Best Practice: The access token is stored directly in the code as a convenience for this tutorial. Do not store credentials directly in source code in a production environment.
Run the solution
Press Command + R to run the app.
If you are using the Xcode simulator your system must meet these minimum requirements: macOS 14 (Sonoma), Xcode 16, iOS 18. If you are using a physical device, then refer to the system requirements.
You should see a scene of hotspots in the Yosemite Valley. Tap to set start and end locations. Long-press and drag to display and move a distance measurement analysis to evaluate the horizontal, vertical, and direct distances between two park locations.
What's next?
Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials: