Learn how to download and display an offline map for a user-defined geographical area of a web map.

Offline maps allow users to continue working when network connectivity is poor or lost. If a web map is enabled for offline use, a user can request that ArcGIS generates an offline map for a specified geographic area of interest.
In this tutorial, you will download an offline map for an area of interest from the web map of the
stormwater network within Naperville, IL, USA
. You can then use this offline map without a network connection.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 web map tutorial. Or download and unzip the Display a web map 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_
element, change the text content to Display an offline map (on-demand).name"> strings.xmlUse dark colors for code blocks <resources> <string name="app_name">Display an offline map (on-demand)</string> </resources>
-
In the Android tool window, open Gradle Scripts > settings.gradle.
Change the value of
root
to "Display an offline map (on-demand)".Project.name settings.gradleUse dark colors for code blocks rootProject.name = "Display an offline map (on-demand)" include ':app'
-
Click File > Sync Project with Gradle files. Android Studio will recognize your changes and create a new .idea folder.
-
-
If you downloaded the
Display a web map
solution project, get your API key and set it in your app.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
set
method, find theA p i K e y F o r App() ArcGISRuntime
call and paste your API key inside the quotes, replacing YOUR_API_KEY.Environment.set A p i Key("YOUR_ API_ KEY") MainActivity.ktUse dark colors for code blocks override fun onDestroy() { mapView.dispose() super.onDestroy() } private fun setApiKeyForApp(){ // 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") }
-
Add import statements and packagingOptions
-
Replace app-specific import statements with the imports needed for this tutorial.
MainActivity.ktUse dark colors for code blocks 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 package com.example.app import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import android.graphics.Color import android.view.View import android.widget.ProgressBar import android.widget.Toast import java.util.* import com.esri.arcgisruntime.ArcGISRuntimeEnvironment import com.esri.arcgisruntime.concurrent.Job import com.esri.arcgisruntime.geometry.Envelope import com.esri.arcgisruntime.geometry.SpatialReferences import com.esri.arcgisruntime.mapping.ArcGISMap 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.portal.Portal import com.esri.arcgisruntime.portal.PortalItem import com.esri.arcgisruntime.symbology.SimpleFillSymbol import com.esri.arcgisruntime.symbology.SimpleLineSymbol import com.esri.arcgisruntime.tasks.offlinemap.GenerateOfflineMapParameters import com.esri.arcgisruntime.tasks.offlinemap.GenerateOfflineMapUpdateMode import com.esri.arcgisruntime.tasks.offlinemap.OfflineMapTask import com.example.app.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() {
-
In Android Studio, in the Android tool window, open Gradle Scripts > build.gradle (Module:Display_a_map_from_a_mobile_map_package.app). Make sure your file matches the one below. In particular, verify that you have the
build
andFeatures packaging
blocks shown.Options build.gradle (Module:Display_a_map_from_a_mobile_map_package.app)Use dark colors for code blocks Copy kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() } buildFeatures { viewBinding true } packagingOptions { exclude 'META-INF/DEPENDENCIES' } }
Get the web map item ID
You can use ArcGIS tools to create and view web maps. Use the Map Viewer to identify the web map item ID. This item ID will be used later in the tutorial.
- Go to the Naperville water network
in the Map Viewer in ArcGIS Online. This web map displays
stormwater network within Naperville, IL, USA
. - Make a note of the item ID at the end of the browser's URL. The item ID should be
acc027394bc84c2fb04d1ed317aac674
Define a Progress Bar
and create read-only properties
-
In the Project tool window, open app > res > layout > activity_main.xml. Add a
<
element to define a progress bar.Progress B a r> MainActivity.ktUse dark colors for code blocks <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="50dp" android:layout_marginEnd="50dp" android:padding="20dp" android:indeterminate="false" android:visibility="gone" android:scaleY="3" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />
-
In the Project tool window, open app > java > com.example.app > MainActivity.kt. At the top of the
Main
class, keep the read-only, lazy propertiesActivity activity
andMain Binding map
that were defined in Display a web map tutorial. Add read-only, lazy propertiesView progress
andBar area
.Overlay MainActivity.ktUse dark colors for code blocks class MainActivity : AppCompatActivity() { private val activityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } private val mapView: MapView by lazy { activityMainBinding.mapView } private val progressBar: ProgressBar by lazy { activityMainBinding.progressBar } private val areaOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
Display the web map
You can display a web map using the web map's item ID. Create a map from the web map portal item, and display it in your app.
-
In the Visual Studio > Solution Explorer, double-click MapViewModel.cs to open the file.
-
In MainActivity.kt, delete the code inside
setup
.Map() Create a
Portal
pointing to ArcGIS Online. Then create aPortalItem
for the Naperville water network, using the portal and the web map's item ID. Finally, set theMap
property to a newView.map ArcGISMap
created using thePortalItem
.MainActivity.ktUse dark colors for code blocks // set up your map here. You will call this method from onCreate() private fun setupMap() { // create a portal pointing to ArcGIS Online val portal = Portal("https://www.arcgis.com", false) // create a portal item for a specific web map id val webMapId = "acc027394bc84c2fb04d1ed317aac674" val mapItem = PortalItem(portal, webMapId) // create the map from the item val map = ArcGISMap(mapItem) // set the map to the map view mapView.map = map }
-
Add the
area
to the map view's list of graphics overlays.Overlay The GraphicsOverlay referenced by
area
has no graphics in it at the moment. You will add an area-of-interest graphic in the next section. To see where you defined theOverlay area
property, expandOverlay Show more lines
in the code block below. The property is highlighted in blue.For more information about the list of graphics overlays in a map view, see
Geo
.View.get Graphics Overlays() MainActivity.ktUse dark colors for code blocks // set up your map here. You will call this method from onCreate() private fun setupMap() { // create a portal pointing to ArcGIS Online val portal = Portal("https://www.arcgis.com", false) // create a portal item for a specific web map id val webMapId = "acc027394bc84c2fb04d1ed317aac674" val mapItem = PortalItem(portal, webMapId) // create the map from the item val map = ArcGISMap(mapItem) // set the map to the map view mapView.map = map // add a graphics overlay to the map view mapView.graphicsOverlays.add(areaOverlay) }
-
Click Run > Run > app to run the app.
You should see a map of the
stormwater network within Naperville, IL, USA
. Use the mouse to drag, scroll, and double-click the map view to explore the map.Specify an area of the web map to take offline
You can specify an area of the web map to take offline using an
Envelope
. You can use a graphic to display the area on the map.
-
Define a new function
create
. Create anAreaof Interest Graphic() Envelope
to define the area of interest to take offline.MainActivity.ktUse dark colors for code blocks private fun createAreaOfInterestGraphic(): Envelope { // define area of interest (envelope) to take offline. val offlineArea = Envelope(-88.1526, 41.7694, -88.1490, 41.7714, SpatialReferences.getWgs84()) }
-
Display a graphic of the area to take offline.
Use a
SimpleLineSymbol
and aSimpleFillSymbol
to create a newGraphic
of the offline area with a red outline. Add the graphic to theGraphicsOverlay
referenced byarea
.Overlay MainActivity.ktUse dark colors for code blocks private fun createAreaOfInterestGraphic(): Envelope { // define area of interest (envelope) to take offline. val offlineArea = Envelope(-88.1526, 41.7694, -88.1490, 41.7714, SpatialReferences.getWgs84()) // create a graphic to display the area to take offline val lineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 2f) val fillSymbol = SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.TRANSPARENT, lineSymbol) val offlineAreaGraphic = Graphic(offlineArea, fillSymbol) // create a graphics overlay and add the graphic areaOverlay.graphics.add(offlineAreaGraphic) }
-
Return the offline area itself. It will be used later when you create parameters for the job that generates and downloads an offline map.
MainActivity.ktUse dark colors for code blocks private fun createAreaOfInterestGraphic(): Envelope { // define area of interest (envelope) to take offline. val offlineArea = Envelope(-88.1526, 41.7694, -88.1490, 41.7714, SpatialReferences.getWgs84()) // create a graphic to display the area to take offline val lineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 2f) val fillSymbol = SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.TRANSPARENT, lineSymbol) val offlineAreaGraphic = Graphic(offlineArea, fillSymbol) // create a graphics overlay and add the graphic areaOverlay.graphics.add(offlineAreaGraphic) return offlineArea }
-
Click Run > Run > app to run the app.
You should see a red outline on the
stormwater network within Naperville, IL, USA
. This indicates the area of the web map that you are going to take offline.Download and display the offline map
You can generate and download an offline map for a specified area of interest using an asynchronous task. When complete, it will provide the offline map for display in your map view.
-
Define a new function
create
. Create anOffline M a p Task A n d Parameters() OfflineMapTask
that references the online map.MainActivity.ktUse dark colors for code blocks private fun createOfflineMapTaskAndParameters(map: ArcGISMap, offlineArea : Envelope) { // create an offline map task using the current map val offlineMapTask = OfflineMapTask(map) }
-
Get default parameters to generate and download the offline map. Since the return value of
create
is a listenable future, add a done listener, where you modify the default parameters to download a read-only offline map.Default Generate Offline M a p Parameters Async() This tutorial does not involve editing and updating the contents of the offline map. When an offline map is editable, metadata is stored in ArcGIS to track and synchronize edits. Setting the
Generate
toOffline M a p Update Mode NO_
avoids the overhead of maintaining this metadata in ArcGIS.UPDATES MainActivity.ktUse dark colors for code blocks private fun createOfflineMapTaskAndParameters(map: ArcGISMap, offlineArea : Envelope) { // create an offline map task using the current map val offlineMapTask = OfflineMapTask(map) // create a default set of parameters for generating the offline map from the area of interest val parametersFuture = offlineMapTask.createDefaultGenerateOfflineMapParametersAsync(offlineArea) parametersFuture.addDoneListener { val parameters = parametersFuture.get().apply { updateMode = GenerateOfflineMapUpdateMode.NO_UPDATES } downloadOfflineMap(offlineMapTask, parameters) } }
-
Define a new function
download
. Set a download location for the offline map.Offline Map() MainActivity.ktUse dark colors for code blocks private fun downloadOfflineMap(offlineMapTask: OfflineMapTask, parameters: GenerateOfflineMapParameters) { // Build a folder path named with today's date/time in the "My Documents" folder. val downloadLocation = getExternalFilesDir(null)?.path + "OfflineMap_" + Calendar.getInstance().time }
This tutorial code creates a new unique folder in your app's data folder, using the current date and time.
-
Create a new
GenerateOfflineMapJob
using theparameters
passed into the function and thedownload
.Location MainActivity.ktUse dark colors for code blocks private fun downloadOfflineMap(offlineMapTask: OfflineMapTask, parameters: GenerateOfflineMapParameters) { // Build a folder path named with today's date/time in the "My Documents" folder. val downloadLocation = getExternalFilesDir(null)?.path + "OfflineMap_" + Calendar.getInstance().time val generateJob = offlineMapTask.generateOfflineMap(parameters, downloadLocation).apply { } }
-
Add a progress changed listener for the generate offline map job.
MainActivity.ktUse dark colors for code blocks private fun downloadOfflineMap(offlineMapTask: OfflineMapTask, parameters: GenerateOfflineMapParameters) { // Build a folder path named with today's date/time in the "My Documents" folder. val downloadLocation = getExternalFilesDir(null)?.path + "OfflineMap_" + Calendar.getInstance().time val generateJob = offlineMapTask.generateOfflineMap(parameters, downloadLocation).apply { addProgressChangedListener { progressBar.progress = progress } } }
-
Add a job done listener for the generate offline map job.
MainActivity.ktUse dark colors for code blocks private fun downloadOfflineMap(offlineMapTask: OfflineMapTask, parameters: GenerateOfflineMapParameters) { // Build a folder path named with today's date/time in the "My Documents" folder. val downloadLocation = getExternalFilesDir(null)?.path + "OfflineMap_" + Calendar.getInstance().time val generateJob = offlineMapTask.generateOfflineMap(parameters, downloadLocation).apply { addProgressChangedListener { progressBar.progress = progress } addJobDoneListener { // replace the current map with the result offline map when the job finishes if (status == Job.Status.SUCCEEDED) { progressBar.visibility = View.GONE Toast.makeText( this@MainActivity, "Showing offline map.", Toast.LENGTH_LONG ).show() val result = result mapView.map = result.offlineMap areaOverlay.graphics.clear() } } } }
-
Start the generate offline map job, display a progress bar, and display a Toast saying that the job is starting.
MainActivity.ktUse dark colors for code blocks } generateJob.start() progressBar.visibility = View.VISIBLE Toast.makeText(this@MainActivity, "Starting generate offline map job.", Toast.LENGTH_LONG).show() }
-
In
setup
, call the functions you defined to create the area of interest graphic and then create the offline map task. First, callMap() create
and then pass the result when you callAreaof Interest Graphic() create
.Offline M a p Task A n d Parameters() MainActivity.ktUse dark colors for code blocks // set the map to the map view mapView.map = map // add a graphics overlay to the map view mapView.graphicsOverlays.add(areaOverlay) // create a graphic showing the area of interest val offlineArea = createAreaOfInterestGraphic() createOfflineMapTaskAndParameters(map, offlineArea) }
-
Click Run > Run > app to run the app.
You should see an offline map for the specified area of the
stormwater network within Naperville, IL, USA
. Remove your network connection and you will still be able to use the mouse to drag, scroll, and double-click the map view to explore this offline map.What's next?
Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials: