Skip to content
View on GitHubSample viewer app

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

Image of geodesic operations

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

Tap 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. Tap elsewhere and a new line will be created.

How it works

  1. Create a Point and display it as a Graphic.
  2. Obtain a new point when a tap occurs on the MapView and add this point as a graphic.
  3. Create a Polyline from the two points.
  4. Execute GeometryEngine.densifyGeodetic(...) by passing in the created polyline then create a graphic from the returned Geometry.
  5. Execute GeometryEngine.distanceGeodeticOrNull(...) by passing in the two points and display the returned distance on the screen.

Relevant API

  • GeometryEngine.densifyGeodetic
  • GeometryEngine.distanceGeodeticOrNull

About the data

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

Tags

densify, distance, geodesic, geodetic

Sample Code

MainActivity.kt
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 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 168 169 170 171 172 173 174 175 176 177 178 179 180 /*  * 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  *  * 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.  *  */  package com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints  import android.os.Bundle import android.util.Log import com.esri.arcgismaps.sample.sampleslib.EdgeToEdgeCompatActivity import androidx.databinding.DataBindingUtil import androidx.lifecycle.lifecycleScope import com.arcgismaps.ApiKey import com.arcgismaps.ArcGISEnvironment import com.arcgismaps.Color import com.arcgismaps.geometry.AngularUnit import com.arcgismaps.geometry.GeodeticCurveType import com.arcgismaps.geometry.GeometryEngine import com.arcgismaps.geometry.LinearUnit.Companion.kilometers import com.arcgismaps.geometry.Point import com.arcgismaps.geometry.Polyline import com.arcgismaps.geometry.SpatialReference import com.arcgismaps.mapping.ArcGISMap import com.arcgismaps.mapping.BasemapStyle import com.arcgismaps.mapping.Viewpoint import com.arcgismaps.mapping.symbology.SimpleLineSymbol import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle import com.arcgismaps.mapping.view.Graphic import com.arcgismaps.mapping.view.GraphicsOverlay import com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints.databinding.ShowGeodesicPathBetweenTwoPointsActivityMainBinding import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.launch import kotlin.math.roundToInt  class MainActivity : EdgeToEdgeCompatActivity() {   // set up data binding for the activity  private val activityMainBinding: ShowGeodesicPathBetweenTwoPointsActivityMainBinding by lazy {  DataBindingUtil.setContentView(this, R.layout.show_geodesic_path_between_two_points_activity_main)  }   // set up data binding for the mapView  private val mapView by lazy {  activityMainBinding.mapView  }   // shows the distance information as a textview  private val infoTextView by lazy {  activityMainBinding.infoTextView  }   // a red marker symbol for the location points  private val locationMarkerSymbol by lazy {  SimpleMarkerSymbol(  SimpleMarkerSymbolStyle.Circle,  Color.red,  10f  )  }   // the marker graphic for the starting location  private val startingLocationMarkerGraphic by lazy {  Graphic(startingPoint, locationMarkerSymbol)  }   // marker graphic for the destination  private val endLocationMarkerGraphic by lazy {  Graphic(symbol = locationMarkerSymbol)  }   // the geodesic path represented as line graphic  private val geodesicPathGraphic by lazy {  val lineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.red, 5f)  Graphic(symbol = lineSymbol)  }   // starting location for the distance calculation  private val startingPoint = Point(-73.7781, 40.6413, SpatialReference.wgs84())    // creates a graphic overlay to draw all graphics  private val graphicsOverlay = GraphicsOverlay()   override fun onCreate(savedInstanceState: Bundle?) {  super.onCreate(savedInstanceState)   // authentication with an API key or named user is  // required to access basemaps and other location services  ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)  lifecycle.addObserver(mapView)   // create and add a map with a navigation night basemap style  val map = ArcGISMap(BasemapStyle.ArcGISImageryStandard).apply {  initialViewpoint = Viewpoint(Point(34.77, -10.24), 20e7)  }  // configure mapView assignments  mapView.apply {  this.map = map  // add the graphics overlay to the mapview  graphicsOverlays.add(graphicsOverlay)  }   // add all our marker graphics to the graphics overlay  graphicsOverlay.graphics.addAll(  listOf(startingLocationMarkerGraphic, endLocationMarkerGraphic, geodesicPathGraphic)  )   lifecycleScope.launch {  // check if the map has loaded successfully  map.load().onSuccess {  // capture and collect when the user taps on the screen  mapView.onSingleTapConfirmed.collect { event ->  event.mapPoint?.let { point -> displayGeodesicPath(point) }  }  }.onFailure {  // if map load failed, show the error  showError("Error Loading Map")  }  }  }   /**  * Displays the destination location marker at the tapped location  * and draws a geodesic path curve using GeometryEngine.densifyGeodetic  * and computes the distance using GeometryEngine.lengthGeodetic  */  private fun displayGeodesicPath(point: Point) {  // project the tapped point into the same spatial reference as source point  val destinationPoint = GeometryEngine.projectOrNull(point, SpatialReference.wgs84())  ?: return showError("Error converting point projection")   // check if the destination point is within the map bounds  // isEmpty returns true if out of bounds  if (!destinationPoint.isEmpty) {  // update the end location marker location on map  endLocationMarkerGraphic.geometry = destinationPoint  // create a polyline between source and destination points  val polyline = Polyline(listOf(startingPoint, destinationPoint))  // generate a geodesic curve using the polyline  val pathGeometry = GeometryEngine.densifyGeodeticOrNull(  geometry = polyline,  maxSegmentLength = 1.0,  lengthUnit = kilometers,  curveType = GeodeticCurveType.Geodesic  // only compute the distance if the returned curved path geometry is not null  ) ?: return showError("Error creating a densified geometry")  // update the path graphic  geodesicPathGraphic.geometry = pathGeometry  // compute the path distance in kilometers  val distance = GeometryEngine.distanceGeodeticOrNull(  startingPoint,  destinationPoint,  distanceUnit = kilometers,  azimuthUnit = AngularUnit.degrees,  curveType = GeodeticCurveType.Geodesic  )  // display the distance result  infoTextView.text = getString(R.string.distance_info_text, distance?.distance?.roundToInt())  }  }   private fun showError(message: String) {  Log.e(localClassName, message)  Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()  } }

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