Download preplanned map area

View on GitHubSample viewer app

Take a map offline using a preplanned map area.

Image of download preplanned map area

Use case

Generating offline maps on demand for a specific area can be time consuming for users and a processing load on the server. If areas of interest are known ahead of time, a web map author can pre-create packages for these areas. This way, the generation only needs to happen once, making the workflow more efficient for users and servers.

An archeology team could define preplanned map areas for dig sites which can be taken offline for field use. To see the difference, compare this sample to the "Generate offline map" sample.

How to use the sample

Select a map area from the Preplanned map areas list. The download progress will be shown and when a download is complete it will be displayed in the map view.

How it works

  1. Open the online ArcGISMap from a PortalItem and display it.
  2. Create an OfflineMapTask using the portal item.
  3. Get the PreplannedMapAreas from the task.
  4. To download a selected map area, create default DownloadPreplannedOfflineMapParameters from the OfflineMapTask using the selected preplanned map area.
  5. Set the update mode of the preplanned map area.
  6. Use the parameters and a local path to create a DownloadPreplannedOfflineMapJob from the task.
  7. Start the DownloadPreplannedOfflineMapJob. Once it has completed, get the DownloadPreplannedOfflineMapResult.
  8. Get the ArcGISMap from the result and display it in the MapView.

Relevant API

  • DownloadPreplannedOfflineMapJob
  • DownloadPreplannedOfflineMapParameters
  • DownloadPreplannedOfflineMapResult
  • OfflineMapTask
  • PreplannedMapArea

About the data

The Naperville stormwater network map is based on ArcGIS Solutions for Stormwater and provides a realistic depiction of a theoretical stormwater network.

Additional information

PreplannedUpdateMode can be used to set the way the preplanned map area receives updates in several ways:

  • NoUpdates - No feature updates will be performed.
  • DownloadScheduledUpdates - Scheduled, read-only updates will be downloaded from the online map area and applied to the local mobile geodatabases.
  • DownloadScheduledUpdatesAndUploadNewFeatures - Scheduled, read-only updates are downloaded from the online map area and applied to the local mobile geodatabases. Newly added features can also be uploaded to the feature service.
  • SyncWithFeatureServices - Changes, including local edits, will be synced directly with the underlying feature services.

For more information about offline workflows, see Offline maps, scenes, and data in the ArcGIS Developers guide.

Tags

map area, offline, pre-planned, preplanned

Sample Code

DownloadPreplannedMapAreaViewModel.ktDownloadPreplannedMapAreaViewModel.ktMainActivity.ktDownloadPreplannedMapAreaScreen.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* 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
 *
 *    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.downloadpreplannedmaparea.components

import android.app.Application
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.PortalItem
import com.arcgismaps.portal.Portal
import com.arcgismaps.tasks.offlinemaptask.DownloadPreplannedOfflineMapJob
import com.arcgismaps.tasks.offlinemaptask.GenerateOfflineMapJob
import com.arcgismaps.tasks.offlinemaptask.OfflineMapTask
import com.arcgismaps.tasks.offlinemaptask.PreplannedMapArea
import com.arcgismaps.tasks.offlinemaptask.PreplannedUpdateMode
import com.esri.arcgismaps.sample.downloadpreplannedmaparea.R
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File

class DownloadPreplannedMapAreaViewModel(application: Application) : AndroidViewModel(application) {

    // The directory where the offline map will be saved
    private val offlineMapPath by lazy {
        application.getExternalFilesDir(null)?.path.toString() + File.separator + application.getString(
            R.string.download_preplanned_map_area_app_name
        )
    }

    // Create a portal to ArcGIS Online
    private val portal = Portal("https://www.arcgis.com")

    // create a portal item using the portal and the item id of a map service
    private val portalItem = PortalItem(portal, "acc027394bc84c2fb04d1ed317aac674")

    private val offlineMapTask = OfflineMapTask(portalItem)

    // A list of preplanned map areas populated by the offline map task
    private var preplannedMapAreas = mutableListOf<PreplannedMapArea>()

    // Keep a hash map of downloaded maps
    private var downloadedMapAreas: HashMap<String, ArcGISMap> = hashMapOf()

    // An online map created from the portal item
    private val onlineMap = ArcGISMap(portalItem)

    // The current map shown in the map view
    var currentMap by mutableStateOf(onlineMap)

    val preplannedMapAreaInfoList = mutableStateListOf<PreplannedMapAreaInfo>()

    // Defined to send messages related to offlineMapJob
    val snackbarHostState = SnackbarHostState()

    // Create a message dialog view model for handling error messages
    val messageDialogVM = MessageDialogViewModel()

    init {
        with(viewModelScope) {
            launch(Dispatchers.IO) {
                offlineMapTask.getPreplannedMapAreas().onSuccess {
                    // Keep a list of all preplanned map areas
                    preplannedMapAreas.addAll(it)

                    // Add all of the preplanned map areas name and download status to a list
                    it.forEachIndexed { index, preplannedMapArea ->
                        preplannedMapAreaInfoList.add(
                            PreplannedMapAreaInfo(
                                index = index,
                                name = preplannedMapArea.portalItem.title,
                                progress = 0f,
                                isDownloaded = false
                            )
                        )
                    }
                }
            }

            launch(Dispatchers.Main) {
                onlineMap.load().onFailure { messageDialogVM.showMessageDialog(it) }
            }
        }
    }

    /**
     * Show the original map from the portal item.
     */
    fun showOnlineMap() {
        currentMap = onlineMap
    }

    /**
     * Download or show the already downloaded preplanned map area.
     */
    fun downloadOrShowOfflineMap(preplannedMapAreaInfo: PreplannedMapAreaInfo) {
        if (preplannedMapAreaInfo.isDownloaded) {
            showOfflineMap(preplannedMapAreaInfo)
        } else {
            downloadOfflineMap(preplannedMapAreaInfo)
        }
    }

    /**
     * Show the offline map of the given preplanned map area name.
     */
    private fun showOfflineMap(preplannedMapAreaInfo: PreplannedMapAreaInfo) {
        downloadedMapAreas[preplannedMapAreaInfo.name]?.let { selectedArcGISMap ->
            currentMap = selectedArcGISMap
        }
    }

    /**
     * Use the [OfflineMapTask] to create [DownloadPreplannedOfflineMapParameters] for the given [PreplannedMapArea].
     * Then use the task to create a [DownloadPreplannedOfflineMapJob] to download the preplanned offline map.
     */
    private fun downloadOfflineMap(preplannedMapAreaInfo: PreplannedMapAreaInfo) {
        viewModelScope.launch(Dispatchers.IO) {
            // Get the area of interest for the preplanned map area
            preplannedMapAreas.find { it.portalItem.title == preplannedMapAreaInfo.name }?.let { preplannedMapArea ->
                // Create default download parameters from the offline map task
                offlineMapTask.createDefaultDownloadPreplannedOfflineMapParameters(preplannedMapArea).onSuccess {
                    // Set the update mode to receive no updates
                    it.updateMode = PreplannedUpdateMode.NoUpdates
                    // Define the path where the map will be saved
                    val downloadDirectoryPath = offlineMapPath + File.separator + preplannedMapAreaInfo.name
                    File(downloadDirectoryPath).mkdirs()
                    // Create a job to download the preplanned offline map
                    val downloadPreplannedOfflineMapJob = offlineMapTask.createDownloadPreplannedOfflineMapJob(
                        parameters = it, downloadDirectoryPath = downloadDirectoryPath
                    )
                    runOfflineMapJob(downloadPreplannedOfflineMapJob, preplannedMapAreaInfo)
                }
            }
        }
    }

    /**
     * Starts the [GenerateOfflineMapJob], shows the progress dialog and displays the result offline map to the MapView.
     */
    private fun runOfflineMapJob(
        downloadPreplannedOfflineMapJob: DownloadPreplannedOfflineMapJob,
        preplannedMapAreaInfo: PreplannedMapAreaInfo
    ) {
        with(viewModelScope) {
            // Collect the job's progress flow
            val progressCoroutine = launch(Dispatchers.IO) {
                downloadPreplannedOfflineMapJob.progress.collect { progress ->
                    // Update the UI to this preplanned map area's downloads progress
                    preplannedMapAreaInfoList[preplannedMapAreaInfo.index] =
                        preplannedMapAreaInfo.copy(progress = progress.toFloat() / 100)
                }
            }
            // Start the job and handle the result
            launch(Dispatchers.IO) {
                // Start the job and wait for Job result
                downloadPreplannedOfflineMapJob.start()
                downloadPreplannedOfflineMapJob.result().onSuccess { downloadedMap ->
                    // Set the offline map result as the displayed
                    currentMap = downloadedMap.offlineMap
                    // Update the UI to show the map as downloaded
                    val index = preplannedMapAreaInfoList.indexOf(preplannedMapAreaInfo)
                    preplannedMapAreaInfoList[preplannedMapAreaInfo.index] =
                        preplannedMapAreaInfo.copy(isDownloaded = true)
                    // Add the downloaded map to the list of downloaded maps
                    downloadedMapAreas[preplannedMapAreaInfo.name] = downloadedMap.offlineMap
                    // Show user where map was locally saved
                    snackbarHostState.showSnackbar(message = "Map saved at: " + downloadPreplannedOfflineMapJob.downloadDirectoryPath)
                }.onFailure { messageDialogVM.showMessageDialog(it) }
                // Cancel the coroutine handling progress reporting
                progressCoroutine.cancel()
            }
        }
    }
}

data class PreplannedMapAreaInfo(val index: Int, val name: String, val progress: Float, val isDownloaded: Boolean)

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