Find a route and directions
Learn how to find a route and directions with the route service.

Routing is the process of finding the path from an origin to a destination in a street network. You can use the Routing service to find routes, get driving directions, calculate drive times, and solve complicated, multiple vehicle routing problems. To create a route, you typically define a set of stops (origin and one or more destinations) and use the service to find a route with directions. You can also use a number of additional parameters such as barriers and mode of travel to refine the results.
In this tutorial, you define an origin and destination by clicking on the map. These values are used to get a route and directions from the route service. The directions are also displayed on the map.
Prerequisites
The following are required for this tutorial:
- An ArcGIS account to access API keys. If you don't have an account, sign up for free.
- Confirm that your system meets the system requirements.
- An IDE for Android development in Kotlin.
Steps
Open an Android Studio project
To start this tutorial, complete the Display a map tutorial, or download and unzip the solution in a new folder.
Modify the old project for use in this new tutorial. Expand More info for instructions.
On your file system, delete the .idea folder, if present, at the top level of your project.
In the Android tool window, open app > res > values > strings.xml.
In the
<string name="app_name">
element, change the text content to Display a web map.strings.xmlChange line 1 2 3<resources> <string name="app_name">Add a point, line, and polygon</string> </resources>
In the Android tool window, open Gradle Scripts > settings.gradle.
Change the value of
rootProject.name
to "Find a route and directions".settings.gradleChange line 1 2include ':app' rootProject.name = "Add a point, line, and polygon"
Click File > Sync Project with Gradle files. Android Studio will recognize your changes and create a new .idea folder.
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 Android Studio: in the Android tool window, open app > java > com.example.app > MainActivity.
In the
setupMap
function, set theapiKey
property on theArcGISRuntimeEnvironment
with your API key.MainActivity.ktChange line Change line Change line Change line 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 80 80/* * Copyright 2020 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. */ package com.example.app import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.MapView import com.example.app.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private val activityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // set your API key // Note: it is not best practice to store API keys in source code. The API key is referenced // here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") // create a map with the BasemapStyle streets val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC) // set the map to be displayed in the layout's MapView mapView.map = map // set the viewpoint, Viewpoint(latitude, longitude, scale) mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0)) } }
Add import statements and class properties
Add import statements and class properties to reference the packages and classes required for this tutorial.
Highlight the existing imports and replace with the imports needed for this tutorial.
MainActivity.ktChange line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line Change line 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 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 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43/* * Copyright 2021 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. */ package com.example.app import android.R.layout import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.MotionEvent import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener import com.esri.arcgisruntime.mapping.view.Graphic import com.esri.arcgisruntime.mapping.view.GraphicsOverlay import com.esri.arcgisruntime.mapping.view.MapView import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask import com.esri.arcgisruntime.tasks.networkanalysis.Stop import com.example.app.databinding.ActivityMainBinding import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { private val activityMainBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val listView: ListView by lazy { activityMainBinding.listView } private val directionsList: MutableList<String> by lazy { mutableListOf("Tap to add two points to the map to find a route between them.") } private val arrayAdapter by lazy { ArrayAdapter(this, layout.simple_list_item_1, directionsList) } private val routeStops: MutableList<Stop> by lazy { mutableListOf() } private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) listView.adapter = arrayAdapter setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") val map = ArcGISMap(BasemapStyle.ARCGIS_STREETS) mapView.apply { // set the map on the map view this.map = map setViewpoint(Viewpoint(34.0539, -118.2453, 144447.638572)) graphicsOverlays.add(graphicsOverlay) onTouchListener = object : DefaultMapViewOnTouchListener(this@MainActivity, mapView) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean { val screenPoint = android.graphics.Point(e.x.roundToInt(), e.y.roundToInt()) when (routeStops.size) { // on first tap, add a stop 0 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) } // on second tap, add a stop and find route between them 1 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) findRoute() Toast.makeText( applicationContext, "Calculating route.", Toast.LENGTH_SHORT ).show() } // on a further tap, clear and add a new first stop else -> { clear() addStop(Stop(mapView.screenToLocation(screenPoint))) } } return true } } } } private fun addStop(stop: Stop) { routeStops.add(stop) // create a blue circle symbol for the stop val stopMarker = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.BLUE, 20f) // get the stop's geometry val routeStopGeometry = stop.geometry // add graphic to graphics overlay graphicsOverlay.graphics.add(Graphic(routeStopGeometry, stopMarker)) } private fun findRoute() { val routeTask = RouteTask( this, "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World" ) val routeParametersFuture = routeTask.createDefaultParametersAsync() routeParametersFuture.addDoneListener { try { val routeParameters = routeParametersFuture.get().apply { isReturnDirections = true setStops(routeStops) } // get the route and display it val routeResultFuture = routeTask.solveRouteAsync(routeParameters) routeResultFuture.addDoneListener { try { val result = routeResultFuture.get() val routes = result.routes if (routes.isNotEmpty()) { val route = routes[0] val shape = route.routeGeometry val routeGraphic = Graphic( shape, SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2f) ) graphicsOverlay.graphics.add(routeGraphic) // get the direction text for each maneuver and display it as a list in the UI directionsList.clear() route.directionManeuvers.forEach { directionsList.add(it.directionText) } arrayAdapter.notifyDataSetChanged() } } catch (e: Exception) { val error = "Error solving route: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } catch (e: Exception) { val error = "Error creating default route parameters: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } private fun clear() { routeStops.clear() graphicsOverlay.graphics.clear() directionsList.clear() directionsList.add("Tap to add two points to the map to find a route between them.") arrayAdapter.notifyDataSetChanged() } }
Within the
MainActivity
class, add the following class properties. These will be used in multiple functions within this tutorial.MainActivity.ktAdd 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. 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 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 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80/* * Copyright 2021 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. */ package com.example.app import android.R.layout import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.MotionEvent import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener import com.esri.arcgisruntime.mapping.view.Graphic import com.esri.arcgisruntime.mapping.view.GraphicsOverlay import com.esri.arcgisruntime.mapping.view.MapView import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask import com.esri.arcgisruntime.tasks.networkanalysis.Stop import com.example.app.databinding.ActivityMainBinding import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { private val activityMainBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val listView: ListView by lazy { activityMainBinding.listView } private val directionsList: MutableList<String> by lazy { mutableListOf("Tap to add two points to the map to find a route between them.") } private val arrayAdapter by lazy { ArrayAdapter(this, layout.simple_list_item_1, directionsList) } private val routeStops: MutableList<Stop> by lazy { mutableListOf() } private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) listView.adapter = arrayAdapter setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") val map = ArcGISMap(BasemapStyle.ARCGIS_STREETS) mapView.apply { // set the map on the map view this.map = map setViewpoint(Viewpoint(34.0539, -118.2453, 144447.638572)) graphicsOverlays.add(graphicsOverlay) onTouchListener = object : DefaultMapViewOnTouchListener(this@MainActivity, mapView) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean { val screenPoint = android.graphics.Point(e.x.roundToInt(), e.y.roundToInt()) when (routeStops.size) { // on first tap, add a stop 0 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) } // on second tap, add a stop and find route between them 1 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) findRoute() Toast.makeText( applicationContext, "Calculating route.", Toast.LENGTH_SHORT ).show() } // on a further tap, clear and add a new first stop else -> { clear() addStop(Stop(mapView.screenToLocation(screenPoint))) } } return true } } } } private fun addStop(stop: Stop) { routeStops.add(stop) // create a blue circle symbol for the stop val stopMarker = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.BLUE, 20f) // get the stop's geometry val routeStopGeometry = stop.geometry // add graphic to graphics overlay graphicsOverlay.graphics.add(Graphic(routeStopGeometry, stopMarker)) } private fun findRoute() { val routeTask = RouteTask( this, "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World" ) val routeParametersFuture = routeTask.createDefaultParametersAsync() routeParametersFuture.addDoneListener { try { val routeParameters = routeParametersFuture.get().apply { isReturnDirections = true setStops(routeStops) } // get the route and display it val routeResultFuture = routeTask.solveRouteAsync(routeParameters) routeResultFuture.addDoneListener { try { val result = routeResultFuture.get() val routes = result.routes if (routes.isNotEmpty()) { val route = routes[0] val shape = route.routeGeometry val routeGraphic = Graphic( shape, SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2f) ) graphicsOverlay.graphics.add(routeGraphic) // get the direction text for each maneuver and display it as a list in the UI directionsList.clear() route.directionManeuvers.forEach { directionsList.add(it.directionText) } arrayAdapter.notifyDataSetChanged() } } catch (e: Exception) { val error = "Error solving route: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } catch (e: Exception) { val error = "Error creating default route parameters: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } private fun clear() { routeStops.clear() graphicsOverlay.graphics.clear() directionsList.clear() directionsList.add("Tap to add two points to the map to find a route between them.") arrayAdapter.notifyDataSetChanged() } }
Update the map
A streets basemap layer is typically used in routing applications. Update the basemap to use the ARCGIS_STREETS
basemap style, change the position of the map to center on Los Angeles and add a GraphicsOverlay
.
In the
setupMap()
function, update theBasemap
style property fromARCGIS_TOPOGRAPHIC
toARCGIS_STREETS
, and update the latitude and longitude coordinates to center on Los Angeles.MainActivity.ktChange line Change line 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 119 120 121 122 123 123 122 121 120 119 118 117 116 115 114 113 112 111 110 109 108 107 106 105 104 103 102 101 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 45/* * Copyright 2021 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. */ package com.example.app import android.R.layout import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.MotionEvent import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener import com.esri.arcgisruntime.mapping.view.Graphic import com.esri.arcgisruntime.mapping.view.GraphicsOverlay import com.esri.arcgisruntime.mapping.view.MapView import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask import com.esri.arcgisruntime.tasks.networkanalysis.Stop import com.example.app.databinding.ActivityMainBinding import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { private val activityMainBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val listView: ListView by lazy { activityMainBinding.listView } private val directionsList: MutableList<String> by lazy { mutableListOf("Tap to add two points to the map to find a route between them.") } private val arrayAdapter by lazy { ArrayAdapter(this, layout.simple_list_item_1, directionsList) } private val routeStops: MutableList<Stop> by lazy { mutableListOf() } private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) listView.adapter = arrayAdapter setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") val map = ArcGISMap(BasemapStyle.ARCGIS_STREETS) mapView.apply { // set the map on the map view this.map = map setViewpoint(Viewpoint(34.0539, -118.2453, 144447.638572)) graphicsOverlays.add(graphicsOverlay) onTouchListener = object : DefaultMapViewOnTouchListener(this@MainActivity, mapView) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean { val screenPoint = android.graphics.Point(e.x.roundToInt(), e.y.roundToInt()) when (routeStops.size) { // on first tap, add a stop 0 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) } // on second tap, add a stop and find route between them 1 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) findRoute() Toast.makeText( applicationContext, "Calculating route.", Toast.LENGTH_SHORT ).show() } // on a further tap, clear and add a new first stop else -> { clear() addStop(Stop(mapView.screenToLocation(screenPoint))) } } return true } } } } private fun addStop(stop: Stop) { routeStops.add(stop) // create a blue circle symbol for the stop val stopMarker = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.BLUE, 20f) // get the stop's geometry val routeStopGeometry = stop.geometry // add graphic to graphics overlay graphicsOverlay.graphics.add(Graphic(routeStopGeometry, stopMarker)) } private fun findRoute() { val routeTask = RouteTask( this, "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World" ) val routeParametersFuture = routeTask.createDefaultParametersAsync() routeParametersFuture.addDoneListener { try { val routeParameters = routeParametersFuture.get().apply { isReturnDirections = true setStops(routeStops) } // get the route and display it val routeResultFuture = routeTask.solveRouteAsync(routeParameters) routeResultFuture.addDoneListener { try { val result = routeResultFuture.get() val routes = result.routes if (routes.isNotEmpty()) { val route = routes[0] val shape = route.routeGeometry val routeGraphic = Graphic( shape, SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2f) ) graphicsOverlay.graphics.add(routeGraphic) // get the direction text for each maneuver and display it as a list in the UI directionsList.clear() route.directionManeuvers.forEach { directionsList.add(it.directionText) } arrayAdapter.notifyDataSetChanged() } } catch (e: Exception) { val error = "Error solving route: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } catch (e: Exception) { val error = "Error creating default route parameters: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } private fun clear() { routeStops.clear() graphicsOverlay.graphics.clear() directionsList.clear() directionsList.add("Tap to add two points to the map to find a route between them.") arrayAdapter.notifyDataSetChanged() } }
Add the
graphicsOverlay
to themapView
. A graphics overlay is a container for graphics. Graphics will be added later in this tutorial as a visual means to display the route stops and route result on the map.MainActivity.ktAdd line. 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 113 114 115 116 117 118 119 120 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 121 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122 122/* * Copyright 2021 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. */ package com.example.app import android.R.layout import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.MotionEvent import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener import com.esri.arcgisruntime.mapping.view.Graphic import com.esri.arcgisruntime.mapping.view.GraphicsOverlay import com.esri.arcgisruntime.mapping.view.MapView import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask import com.esri.arcgisruntime.tasks.networkanalysis.Stop import com.example.app.databinding.ActivityMainBinding import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { private val activityMainBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val listView: ListView by lazy { activityMainBinding.listView } private val directionsList: MutableList<String> by lazy { mutableListOf("Tap to add two points to the map to find a route between them.") } private val arrayAdapter by lazy { ArrayAdapter(this, layout.simple_list_item_1, directionsList) } private val routeStops: MutableList<Stop> by lazy { mutableListOf() } private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) listView.adapter = arrayAdapter setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") val map = ArcGISMap(BasemapStyle.ARCGIS_STREETS) mapView.apply { // set the map on the map view this.map = map setViewpoint(Viewpoint(34.0539, -118.2453, 144447.638572)) graphicsOverlays.add(graphicsOverlay) onTouchListener = object : DefaultMapViewOnTouchListener(this@MainActivity, mapView) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean { val screenPoint = android.graphics.Point(e.x.roundToInt(), e.y.roundToInt()) when (routeStops.size) { // on first tap, add a stop 0 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) } // on second tap, add a stop and find route between them 1 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) findRoute() Toast.makeText( applicationContext, "Calculating route.", Toast.LENGTH_SHORT ).show() } // on a further tap, clear and add a new first stop else -> { clear() addStop(Stop(mapView.screenToLocation(screenPoint))) } } return true } } } } private fun addStop(stop: Stop) { routeStops.add(stop) // create a blue circle symbol for the stop val stopMarker = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.BLUE, 20f) // get the stop's geometry val routeStopGeometry = stop.geometry // add graphic to graphics overlay graphicsOverlay.graphics.add(Graphic(routeStopGeometry, stopMarker)) } private fun findRoute() { val routeTask = RouteTask( this, "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World" ) val routeParametersFuture = routeTask.createDefaultParametersAsync() routeParametersFuture.addDoneListener { try { val routeParameters = routeParametersFuture.get().apply { isReturnDirections = true setStops(routeStops) } // get the route and display it val routeResultFuture = routeTask.solveRouteAsync(routeParameters) routeResultFuture.addDoneListener { try { val result = routeResultFuture.get() val routes = result.routes if (routes.isNotEmpty()) { val route = routes[0] val shape = route.routeGeometry val routeGraphic = Graphic( shape, SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2f) ) graphicsOverlay.graphics.add(routeGraphic) // get the direction text for each maneuver and display it as a list in the UI directionsList.clear() route.directionManeuvers.forEach { directionsList.add(it.directionText) } arrayAdapter.notifyDataSetChanged() } } catch (e: Exception) { val error = "Error solving route: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } catch (e: Exception) { val error = "Error creating default route parameters: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } private fun clear() { routeStops.clear() graphicsOverlay.graphics.clear() directionsList.clear() directionsList.add("Tap to add two points to the map to find a route between them.") arrayAdapter.notifyDataSetChanged() } }
Add a ListView
to display driving directions
To display the turn-by-turn directions from the route, use an Android list view to display it on the screen.
In the
activity_main.xml
file, create an AndroidListView
which will display a vertical list of directions.activity_main.xmlAdd line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. 13 13 13 13 13 13 13 13 13 13 13 13 13 14 15 16 17 18 19 20 21 22 22 22<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.esri.arcgisruntime.mapping.view.MapView android:id="@+id/mapView" android:layout_width="match_parent" android:layout_height="match_parent" /> <ListView android:id="@+id/listView" android:layout_width="0dp" android:layout_height="0dp" android:background="@android:drawable/screen_background_light" app:layout_constraintHeight_percent="0.35" app:layout_constraintWidth_percent="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
In
MainActivity.kt
, in theonCreate()
function, set thearrayAdapter
to thelistView
. TheArrayAdapter
will create and populate thelistView
withTextView
s as required.MainActivity.ktAdd line. 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 78 79 80 81 82 83 84 85 86 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 58 58 58 58 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -20/* * Copyright 2021 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. */ package com.example.app import android.R.layout import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.MotionEvent import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener import com.esri.arcgisruntime.mapping.view.Graphic import com.esri.arcgisruntime.mapping.view.GraphicsOverlay import com.esri.arcgisruntime.mapping.view.MapView import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask import com.esri.arcgisruntime.tasks.networkanalysis.Stop import com.example.app.databinding.ActivityMainBinding import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { private val activityMainBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val listView: ListView by lazy { activityMainBinding.listView } private val directionsList: MutableList<String> by lazy { mutableListOf("Tap to add two points to the map to find a route between them.") } private val arrayAdapter by lazy { ArrayAdapter(this, layout.simple_list_item_1, directionsList) } private val routeStops: MutableList<Stop> by lazy { mutableListOf() } private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) listView.adapter = arrayAdapter setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") val map = ArcGISMap(BasemapStyle.ARCGIS_STREETS) mapView.apply { // set the map on the map view this.map = map setViewpoint(Viewpoint(34.0539, -118.2453, 144447.638572)) graphicsOverlays.add(graphicsOverlay) onTouchListener = object : DefaultMapViewOnTouchListener(this@MainActivity, mapView) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean { val screenPoint = android.graphics.Point(e.x.roundToInt(), e.y.roundToInt()) when (routeStops.size) { // on first tap, add a stop 0 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) } // on second tap, add a stop and find route between them 1 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) findRoute() Toast.makeText( applicationContext, "Calculating route.", Toast.LENGTH_SHORT ).show() } // on a further tap, clear and add a new first stop else -> { clear() addStop(Stop(mapView.screenToLocation(screenPoint))) } } return true } } } } private fun addStop(stop: Stop) { routeStops.add(stop) // create a blue circle symbol for the stop val stopMarker = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.BLUE, 20f) // get the stop's geometry val routeStopGeometry = stop.geometry // add graphic to graphics overlay graphicsOverlay.graphics.add(Graphic(routeStopGeometry, stopMarker)) } private fun findRoute() { val routeTask = RouteTask( this, "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World" ) val routeParametersFuture = routeTask.createDefaultParametersAsync() routeParametersFuture.addDoneListener { try { val routeParameters = routeParametersFuture.get().apply { isReturnDirections = true setStops(routeStops) } // get the route and display it val routeResultFuture = routeTask.solveRouteAsync(routeParameters) routeResultFuture.addDoneListener { try { val result = routeResultFuture.get() val routes = result.routes if (routes.isNotEmpty()) { val route = routes[0] val shape = route.routeGeometry val routeGraphic = Graphic( shape, SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2f) ) graphicsOverlay.graphics.add(routeGraphic) // get the direction text for each maneuver and display it as a list in the UI directionsList.clear() route.directionManeuvers.forEach { directionsList.add(it.directionText) } arrayAdapter.notifyDataSetChanged() } } catch (e: Exception) { val error = "Error solving route: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } catch (e: Exception) { val error = "Error creating default route parameters: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } private fun clear() { routeStops.clear() graphicsOverlay.graphics.clear() directionsList.clear() directionsList.add("Tap to add two points to the map to find a route between them.") arrayAdapter.notifyDataSetChanged() } }
Create an addStops
function
A RouteTask
requires at least a single origin and destination stop to find a route. Create a function to handle user interaction with the map and to add stops and display them as graphics when the map is tapped.
When a user taps on the map, a stop will be added to a list of route stops. In this tutorial, the first tap will create the origin stop and the second will create the destination stop.
Create an
addStop(stop)
function.MainActivity.ktAdd line. Add line. Add line. Add line. 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 154 153 152 151 150 149 148 147 146 145 144 143 142 141 140 139 138 137 136 135 134 133 132 131 130 129 128 127 126 126 126 126 126 126 127 128 128 128 128 128 128 128 128 128 128 129 130 130 129 128 127 126 125 124 123 122 121 120 119 118 117 116 115 114 113 112 111 110 109 108 107 106 105 104 103 102 101 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 66/* * Copyright 2021 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. */ package com.example.app import android.R.layout import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.MotionEvent import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener import com.esri.arcgisruntime.mapping.view.Graphic import com.esri.arcgisruntime.mapping.view.GraphicsOverlay import com.esri.arcgisruntime.mapping.view.MapView import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask import com.esri.arcgisruntime.tasks.networkanalysis.Stop import com.example.app.databinding.ActivityMainBinding import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { private val activityMainBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val listView: ListView by lazy { activityMainBinding.listView } private val directionsList: MutableList<String> by lazy { mutableListOf("Tap to add two points to the map to find a route between them.") } private val arrayAdapter by lazy { ArrayAdapter(this, layout.simple_list_item_1, directionsList) } private val routeStops: MutableList<Stop> by lazy { mutableListOf() } private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) listView.adapter = arrayAdapter setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") val map = ArcGISMap(BasemapStyle.ARCGIS_STREETS) mapView.apply { // set the map on the map view this.map = map setViewpoint(Viewpoint(34.0539, -118.2453, 144447.638572)) graphicsOverlays.add(graphicsOverlay) onTouchListener = object : DefaultMapViewOnTouchListener(this@MainActivity, mapView) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean { val screenPoint = android.graphics.Point(e.x.roundToInt(), e.y.roundToInt()) when (routeStops.size) { // on first tap, add a stop 0 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) } // on second tap, add a stop and find route between them 1 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) findRoute() Toast.makeText( applicationContext, "Calculating route.", Toast.LENGTH_SHORT ).show() } // on a further tap, clear and add a new first stop else -> { clear() addStop(Stop(mapView.screenToLocation(screenPoint))) } } return true } } } } private fun addStop(stop: Stop) { routeStops.add(stop) // create a blue circle symbol for the stop val stopMarker = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.BLUE, 20f) // get the stop's geometry val routeStopGeometry = stop.geometry // add graphic to graphics overlay graphicsOverlay.graphics.add(Graphic(routeStopGeometry, stopMarker)) } private fun findRoute() { val routeTask = RouteTask( this, "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World" ) val routeParametersFuture = routeTask.createDefaultParametersAsync() routeParametersFuture.addDoneListener { try { val routeParameters = routeParametersFuture.get().apply { isReturnDirections = true setStops(routeStops) } // get the route and display it val routeResultFuture = routeTask.solveRouteAsync(routeParameters) routeResultFuture.addDoneListener { try { val result = routeResultFuture.get() val routes = result.routes if (routes.isNotEmpty()) { val route = routes[0] val shape = route.routeGeometry val routeGraphic = Graphic( shape, SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2f) ) graphicsOverlay.graphics.add(routeGraphic) // get the direction text for each maneuver and display it as a list in the UI directionsList.clear() route.directionManeuvers.forEach { directionsList.add(it.directionText) } arrayAdapter.notifyDataSetChanged() } } catch (e: Exception) { val error = "Error solving route: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } catch (e: Exception) { val error = "Error creating default route parameters: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } private fun clear() { routeStops.clear() graphicsOverlay.graphics.clear() directionsList.clear() directionsList.add("Tap to add two points to the map to find a route between them.") arrayAdapter.notifyDataSetChanged() } }
Add the
stop
to theMutableList<Stop>
calledrouteStops
.MainActivity.ktAdd line. 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 154 153 152 151 150 149 148 147 146 145 144 143 142 141 140 139 138 137 136 135 134 133 132 131 130 129 128 127 126 126 126 126 126 126 127 128 129 130 130 130 130 130 130 130 130 131 132 132 131 130 129 128 127 126 125 124 123 122 121 120 119 118 117 116 115 114 113 112 111 110 109 108 107 106 105 104 103 102 101 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 68/* * Copyright 2021 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. */ package com.example.app import android.R.layout import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.MotionEvent import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener import com.esri.arcgisruntime.mapping.view.Graphic import com.esri.arcgisruntime.mapping.view.GraphicsOverlay import com.esri.arcgisruntime.mapping.view.MapView import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask import com.esri.arcgisruntime.tasks.networkanalysis.Stop import com.example.app.databinding.ActivityMainBinding import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { private val activityMainBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val listView: ListView by lazy { activityMainBinding.listView } private val directionsList: MutableList<String> by lazy { mutableListOf("Tap to add two points to the map to find a route between them.") } private val arrayAdapter by lazy { ArrayAdapter(this, layout.simple_list_item_1, directionsList) } private val routeStops: MutableList<Stop> by lazy { mutableListOf() } private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) listView.adapter = arrayAdapter setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") val map = ArcGISMap(BasemapStyle.ARCGIS_STREETS) mapView.apply { // set the map on the map view this.map = map setViewpoint(Viewpoint(34.0539, -118.2453, 144447.638572)) graphicsOverlays.add(graphicsOverlay) onTouchListener = object : DefaultMapViewOnTouchListener(this@MainActivity, mapView) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean { val screenPoint = android.graphics.Point(e.x.roundToInt(), e.y.roundToInt()) when (routeStops.size) { // on first tap, add a stop 0 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) } // on second tap, add a stop and find route between them 1 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) findRoute() Toast.makeText( applicationContext, "Calculating route.", Toast.LENGTH_SHORT ).show() } // on a further tap, clear and add a new first stop else -> { clear() addStop(Stop(mapView.screenToLocation(screenPoint))) } } return true } } } } private fun addStop(stop: Stop) { routeStops.add(stop) // create a blue circle symbol for the stop val stopMarker = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.BLUE, 20f) // get the stop's geometry val routeStopGeometry = stop.geometry // add graphic to graphics overlay graphicsOverlay.graphics.add(Graphic(routeStopGeometry, stopMarker)) } private fun findRoute() { val routeTask = RouteTask( this, "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World" ) val routeParametersFuture = routeTask.createDefaultParametersAsync() routeParametersFuture.addDoneListener { try { val routeParameters = routeParametersFuture.get().apply { isReturnDirections = true setStops(routeStops) } // get the route and display it val routeResultFuture = routeTask.solveRouteAsync(routeParameters) routeResultFuture.addDoneListener { try { val result = routeResultFuture.get() val routes = result.routes if (routes.isNotEmpty()) { val route = routes[0] val shape = route.routeGeometry val routeGraphic = Graphic( shape, SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2f) ) graphicsOverlay.graphics.add(routeGraphic) // get the direction text for each maneuver and display it as a list in the UI directionsList.clear() route.directionManeuvers.forEach { directionsList.add(it.directionText) } arrayAdapter.notifyDataSetChanged() } } catch (e: Exception) { val error = "Error solving route: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } catch (e: Exception) { val error = "Error creating default route parameters: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } private fun clear() { routeStops.clear() graphicsOverlay.graphics.clear() directionsList.clear() directionsList.add("Tap to add two points to the map to find a route between them.") arrayAdapter.notifyDataSetChanged() } }
Create a
SimpleMarkerSymbol
and use thestop
'sGeometry
to create a newGraphic
and add it to thegraphicsOverlay
.MainActivity.ktAdd line. Add line. Add line. Add line. Add line. Add line. 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 155 154 153 152 151 150 149 148 147 146 145 144 143 142 141 140 139 138 137 136 135 134 133 132 131 130 129 128 127 126 126 126 126 126 126 127 128 129 130 131 132 133 134 135 136 137 138 139 139 138 137 136 135 134 133 132 131 130 129 128 127 126 125 124 123 122 121 120 119 118 117 116 115 114 113 112 111 110 109 108 107 106 105 104 103 102 101 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 75/* * Copyright 2021 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. */ package com.example.app import android.R.layout import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.MotionEvent import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.mapping.ArcGISMap import com.esri.arcgisruntime.mapping.BasemapStyle import com.esri.arcgisruntime.mapping.Viewpoint import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener import com.esri.arcgisruntime.mapping.view.Graphic import com.esri.arcgisruntime.mapping.view.GraphicsOverlay import com.esri.arcgisruntime.mapping.view.MapView import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask import com.esri.arcgisruntime.tasks.networkanalysis.Stop import com.example.app.databinding.ActivityMainBinding import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { private val activityMainBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val listView: ListView by lazy { activityMainBinding.listView } private val directionsList: MutableList<String> by lazy { mutableListOf("Tap to add two points to the map to find a route between them.") } private val arrayAdapter by lazy { ArrayAdapter(this, layout.simple_list_item_1, directionsList) } private val routeStops: MutableList<Stop> by lazy { mutableListOf() } private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(activityMainBinding.root) listView.adapter = arrayAdapter setupMap() } override fun onPause() { mapView.pause() super.onPause() } override fun onResume() { super.onResume() mapView.resume() } override fun onDestroy() { mapView.dispose() super.onDestroy() } // set up your map here. You will call this method from onCreate() private fun setupMap() { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY") val map = ArcGISMap(BasemapStyle.ARCGIS_STREETS) mapView.apply { // set the map on the map view this.map = map setViewpoint(Viewpoint(34.0539, -118.2453, 144447.638572)) graphicsOverlays.add(graphicsOverlay) onTouchListener = object : DefaultMapViewOnTouchListener(this@MainActivity, mapView) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean { val screenPoint = android.graphics.Point(e.x.roundToInt(), e.y.roundToInt()) when (routeStops.size) { // on first tap, add a stop 0 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) } // on second tap, add a stop and find route between them 1 -> { addStop(Stop(mapView.screenToLocation(screenPoint))) findRoute() Toast.makeText( applicationContext, "Calculating route.", Toast.LENGTH_SHORT ).show() } // on a further tap, clear and add a new first stop else -> { clear() addStop(Stop(mapView.screenToLocation(screenPoint))) } } return true } } } } private fun addStop(stop: Stop) { routeStops.add(stop) // create a blue circle symbol for the stop val stopMarker = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.BLUE, 20f) // get the stop's geometry val routeStopGeometry = stop.geometry // add graphic to graphics overlay graphicsOverlay.graphics.add(Graphic(routeStopGeometry, stopMarker)) } private fun findRoute() { val routeTask = RouteTask( this, "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World" ) val routeParametersFuture = routeTask.createDefaultParametersAsync() routeParametersFuture.addDoneListener { try { val routeParameters = routeParametersFuture.get().apply { isReturnDirections = true setStops(routeStops) } // get the route and display it val routeResultFuture = routeTask.solveRouteAsync(routeParameters) routeResultFuture.addDoneListener { try { val result = routeResultFuture.get() val routes = result.routes if (routes.isNotEmpty()) { val route = routes[0] val shape = route.routeGeometry val routeGraphic = Graphic( shape, SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2f) ) graphicsOverlay.graphics.add(routeGraphic) // get the direction text for each maneuver and display it as a list in the UI directionsList.clear() route.directionManeuvers.forEach { directionsList.add(it.directionText) } arrayAdapter.notifyDataSetChanged() } } catch (e: Exception) { val error = "Error solving route: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } catch (e: Exception) { val error = "Error creating default route parameters: " + e.message Toast.makeText(this, error, Toast.LENGTH_LONG).show() Log.e(MainActivity::class.simpleName, error) } } } private fun clear() { routeStops.clear() graphicsOverlay.graphics.clear() directionsList.clear() directionsList.add("Tap to add two points to the map to find a route between them.") arrayAdapter.notifyDataSetChanged() } }
Create a findRoute
function
A route task makes a request to a service and returns the results. Solve the route between the origin and destination stops using a RouteTask
, which accesses a routing service.
Create a
findRoute()
function.MainActivity.ktAdd line. Add line. Add line. Add line. 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169 169