Skip to content

Show geodesic path between two points

View on GitHub

Calculate a geodesic path between two points and measure its distance.

Image of show geodesic path between two points sample

Use case

A geodesic distance provides an accurate, real-world distance between two points. Visualizing flight paths between cities is a common example of a geodesic operation since the flight path between two airports takes into account the curvature of the earth, rather than following the planar path between those points, which appears as a straight line on a projected map.

How to use the sample

Click anywhere on the map. A line graphic will display the geodesic line between the two points. In addition, text that indicates the geodesic distance between the two points will be updated. Click elsewhere and a new line will be created.

How it works

  1. Create a Point and display it as a Graphic when the MapView is tapped.
  2. Obtain a new point when another tap occurs on the MapView and add this point as a graphic.
  3. Create a Polyline from the two points.
  4. Execute GeometryEngine.geodeticDensify(_:maxSegmentLength:lengthUnit:curveType:) by passing in the created polyline then create a graphic from the returned Geometry.
  5. Execute GeometryEngine.geodeticDistance(from:to:distanceUnit:azimuthUnit:curveType:) by passing in the two points and display the returned length on the screen.

Relevant API

  • GeometryEngine.geodeticDensify(_:maxSegmentLength:lengthUnit:curveType:)
  • GeometryEngine.geodeticDistance(from:to:distanceUnit:azimuthUnit:curveType:)
  • MapView.onSingleTapGesture(perform:)

About the data

The Imagery basemap provides the global context for the displayed geodesic line.

Tags

densify, distance, geodesic, geodetic

Sample Code

ShowGeodesicPathBetweenTwoPointsView.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 // Copyright 2025 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 ShowGeodesicPathBetweenTwoPointsView: View {  /// The map that will be displayed in the map view.  @State private var map = Map(basemapStyle: .arcGISImageryStandard)   /// The graphics overlay that will be displayed on the map view.  /// This will hold the graphics that show the start point, end point,  /// and geodesic path.  @State private var overlay = GraphicsOverlay()   /// The current measurement state.  @State private var state: MeasurementState = .notStarted   /// The symbology for point graphics.  private let pointSymbol: Symbol = SimpleMarkerSymbol(style: .cross, color: .blue, size: 20)   /// The symbology for the line graphic.  private let lineSymbol: Symbol = SimpleLineSymbol(style: .dash, color: .yellow, width: 2)   var body: some View {  MapView(map: map, graphicsOverlays: [overlay])  .onSingleTapGesture { _, mapPoint in  state = switch state {  case .notStarted, .complete:  // If the state is empty or complete, then start a new  // path, adding the tap point as the first graphic.  .startOnly(start: mapPoint)  case .startOnly(let start):  // If the state was started, then add the end point  // to complete it.  .complete(start: start, end: mapPoint)  }  }  .overlay(alignment: .top) {  Group {  switch state {  case .notStarted, .startOnly:  Text("Tap on the map to show a geodesic path")  case .complete(_, _, _, let length):  Text(length, format: .measurement(width: .abbreviated))  }  }  .padding()  .frame(maxWidth: .infinity)  .background(.ultraThinMaterial)  }  .onChange(of: state) { updateGraphicsOverlay() }  .animation(.default, value: state)  }   /// Update the graphics overlay for the current state.  private func updateGraphicsOverlay() {  overlay.removeAllGraphics()   switch state {  case .notStarted:  break  case .startOnly(let start):  overlay.addGraphic(Graphic(geometry: start, symbol: pointSymbol))  case .complete(let start, let end, let line, _):  overlay.addGraphic(Graphic(geometry: start, symbol: pointSymbol))  overlay.addGraphic(Graphic(geometry: end, symbol: pointSymbol))  overlay.addGraphic(Graphic(geometry: line, symbol: lineSymbol))  }  } }  extension ShowGeodesicPathBetweenTwoPointsView {  /// A value that represents the measurement state of the view.  enum MeasurementState: Equatable {  /// No measurement started.  case notStarted  /// Only have a starting point.  case startOnly(start: Point)  /// Completed measurement.  case complete(start: Point, end: Point, line: Polyline, distance: Measurement<UnitLength>)   /// Creates a `complete` measurement state with a start and end point,  /// calculating the line and length.  static func complete(start: Point, end: Point) -> Self {  // Create a geodesic line from the start, end points.  let geodesicLine = GeometryEngine.geodeticDensify(  Polyline(points: [start, end]),  maxSegmentLength: 1,  lengthUnit: .kilometers,  curveType: .geodesic  ) as! Polyline   // Calculate the geodesic distance between the two points.  let geodesicDistance = GeometryEngine.geodeticDistance(  from: start,  to: end,  distanceUnit: .meters,  azimuthUnit: .degrees,  curveType: .geodesic  )!   return complete(  start: start,  end: end,  line: geodesicLine,  distance: geodesicDistance.distance  )  }  } }  #Preview {  ShowGeodesicPathBetweenTwoPointsView() }

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