Get elevation at point on surface

View on GitHub

Get the elevation for a given point on a surface in a scene.

Screenshot of get elevation at point on surface sample

Use case

Knowing the elevation at a given point in a landscape can aid in navigation, planning and survey in the field.

How to use the sample

Tap anywhere on the surface to get the elevation at that point. Tap again to dismiss the callout. Elevation is reported in the standard local measurements. The scene view is in WGS84, which is a geographic coordinate system.

How it works

  1. Create a SceneView and a Scene with an arcGISImagery basemap.
  2. Set the elevation source of the scene's base surface.
  3. Convert the tapped screen point into a point on surface.
  4. Asynchronously get the elevation using the Surface.elevation(at:) method and display the result.

Relevant API

  • ArcGISTiledElevationSource
  • ElevationSource
  • SceneView
  • Surface

Tags

elevation, point, surface

Sample Code

GetElevationAtPointOnSurfaceView.swift
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2023 Esri
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   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 ArcGIS
import SwiftUI

struct GetElevationAtPointOnSurfaceView: View {
    /// The view model for the sample.
    @StateObject private var model = Model()

    /// The location callout placement.
    @State private var calloutPlacement: CalloutPlacement?

    /// The screen point where to get the base surface location.
    @State private var screenPoint: CGPoint?

    /// The scene point where the scene was tapped.
    @State private var scenePoint: Point?

    /// The surface elevation of the tapped point.
    @State private var elevation: Double?

    var body: some View {
        SceneViewReader { sceneViewProxy in
            SceneView(scene: model.scene)
                .onSingleTapGesture { screenPoint, scenePoint in
                    self.screenPoint = screenPoint
                    self.scenePoint = scenePoint
                }
                .callout(placement: $calloutPlacement.animation(.default.speed(2))) { _ in
                    VStack(alignment: .leading) {
                        Text("Elevation")
                            .font(.headline)
                        Text(model.elevationString(elevation: elevation))
                            .font(.callout)
                    }
                    .padding(5)
                }
                .task(id: scenePoint) {
                    guard let scenePoint, let screenPoint else { return }
                    if calloutPlacement == nil {
                        // Converts the tapped screen point into a point on the surface.
                        guard let relativeSurfacePoint = sceneViewProxy.baseSurfaceLocation(fromScreenPoint: screenPoint) else { return }
                        // Gets the elevation from the tap location.
                        elevation = await elevation(at: relativeSurfacePoint)
                        // Shows the callout at the tapped location.
                        calloutPlacement = CalloutPlacement.location(scenePoint)
                    } else {
                        // Hides the callout.
                        calloutPlacement = nil
                    }
                }
        }
    }

    /// Gets the elevation at the surface point.
    /// - Parameter surfacePoint: A geographical location on the base surface
    /// in the same spatial reference as the scene view.
    /// - Returns: The elevation in meters.
    private func elevation(at surfacePoint: Point) async -> Double? {
        try? await model.scene.baseSurface.elevation(at: surfacePoint)
    }
}

private extension GetElevationAtPointOnSurfaceView {
    /// The model used to store the geo model and other expensive objects
    /// used in this view.
    class Model: ObservableObject {
        /// A scene with imagery basemap.
        let scene: ArcGIS.Scene = {
            let scene = Scene(basemapStyle: .arcGISImagery)

            // Sets the initial viewpoint of the scene to northern Nepal.
            scene.initialViewpoint = Viewpoint(
                latitude: .nan,
                longitude: .nan,
                scale: .nan,
                camera: Camera(
                    latitude: 28.42,
                    longitude: 83.9,
                    altitude: 1e4,
                    heading: 10,
                    pitch: 80,
                    roll: 0
                )
            )

            // Creates a surface.
            let surface = Surface()

            // Adds the elevation source to the surface.
            surface.addElevationSource(.worldElevationSource)

            // Sets the surface to the scene's base surface.
            scene.baseSurface = surface
            return scene
        }()

        /// A formatter that provides localized descriptions of a linear distance.
        private let lengthFormatter = LengthFormatter()

        /// Gets a elevation description from an optional value.
        /// - Parameter elevation: The optional elevation.
        /// - Returns: A localized elevation string, or "invalid" if it is `nil`.
        func elevationString(elevation: Double?) -> String {
            if let elevation {
                return lengthFormatter.string(fromMeters: elevation)
            } else {
                return "Invalid Elevation"
            }
        }
    }
}

private extension ElevationSource {
    /// A tiled elevation source that provides global elevation.
    static var worldElevationSource: ArcGISTiledElevationSource {
        .init(url: URL(string: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer")!)
    }
}

#Preview {
    GetElevationAtPointOnSurfaceView()
}

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