Offline maps

You can build applications that can display and edit features without a network connection using offline maps.

Offline maps

What is an offline map?

An offline map is a map downloaded from a web map. It also includes data content that is referenced by layers in the offline map. You can use an offline map to display a map, edit data, and analyze data without a network connection.

You use offline maps to build offline applications that:

  • Provide detailed, editable, interactive maps to large mobile workforces.
  • View, collect and edit data in remote areas where network connectivity is unpredictable or unavailable.
  • Support disaster recovery operations when infrastructure has been damaged.
  • Keep map data up to date with the latest edits from mobile workers.
  • Keep mobile workers updated with the latest data and maps.

An offline map shares capabilities with the source web map:

  • Zoom, pan, and rotate the map.
  • Interact with the map's layers.
  • Edit data in feature layers.
  • Query and identify features in feature layers.
  • Display popups as configured in the web map.
  • Control feature layer styling.
  • Control the visible scale ranges for layers.
  • Read preconfigured bookmarks.

How an offline map works

To use an offline map, you begin with a web map. The web map definition and the layer contents are downloaded as an offline map which can be used without a network connection. The web map must be offline enabled.

Layers that reference the following services can have their data content included in the offline map:

  • The basemap layer service (including default basemap styles and custom basemap styles created using the Vector Tile Style Editor).
  • A feature service that has been offline enabled. This includes features, non-spatial data, related records, and attachments.
  • A vector tile service that has been offline enabled.
  • An image tile service that has been offline enabled.

Other layers This includes layers that reference feature services, vector tile services, or image tile services that have not been offline enabled. can be included in an offline map but continue to reference the online data source. These layers can be used whenever the offline application has a network connection.

In addition to data layers, a web map can include tables of non-spatial data. A non-spatial table typically contains rows of data related to features in the web map's data layers. This non-spatial data content can also be included in an offline map.

You can use offline maps to edit features, non-spatial data, related records, and attachments without a network connection. You keep an offline map's data content current with the content of the source web map's layers by synchronizing the offline map and the web map. A network connection is required to synchronize.

There are two approaches to downloading an offline map from a web map:

  • On-demand (specify a custom area of a web map):

    In this approach, an offline application requests a custom geographic area of a web map as an offline map. An offline map is generated and downloaded for this geographic area.

  • Preplanned (select a predefined area of a web map) Preplanned offline maps currently require OAuth authentication. API key support is expected Q2 2021.:

    In this approach, the owner of a web map must first define geographic areas of the web map to be packaged as offline maps.

Steps to work with an offline map

The typical high level steps to working with an offline map are:

  1. Create a web map and prepare it for offline use.
  2. Download an area of the web map as an offline map.
  3. Display and interact with the offline map. Optionally edit features, non-spatial data, and attachments.
  4. Synchronize the offline map with the source web map as needed.

This document covers steps 2-4.

Download

Your offline application should first reference an offline enabled web map. You can then either:

  • Request that an on-demand offline map be generated for a custom geographic area, wait for it to be generated, and then download it.
  • Download a preplanned offline map, if available Preplanned offline maps currently require OAuth authentication. API key support is expected Q2 2021..

You specify where to store a downloaded offline map on the local device. Offline maps remain on the device until you explicitly delete them from the device's file system.

How to download and display an on-demand offline map

The steps to download an on-demand offline map from a web map are:

  1. Create an Offline Map Task referencing the web map. This task object reads the offline properties of the web map and manages downloading an offline map from it.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  2. Get a set of default Generate Offline Map Parameters. These parameters describe how the owner of the web map configured the offline map to be generated and downloaded. You can modify the parameters to fine tune the downloaded offline map.

    Options include whether to download the basemap or use one that has already been downloaded, how to handle attachments, how to handle related records, whether to include data content for editable layers (useful if an offline application focuses on collection new data), and whether to disable offline editing even if the source web map supports editing. To learn more, see Optimize an offline map.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble

    If the map includes layers that use image tiles, then it is important to specify a scale range for downloaded image tiles. Use the overload constructor and specify minimum scale and maximum scale:

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  3. Use the parameters to create a Generate Offline Map Job to download the offline map, specifying the download location.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  4. Start the job and wait for it to complete, returning a Map, which is ready for use.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble

How to download and display a preplanned offline map

The steps to download a preplanned offline map from a web map are:

  1. Create an Offline Map Task referencing the web map. This task object reads the offline properties of the web map and manages downloading an offline map from it.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  2. Use the Offline Map Task to list any available preplanned offline maps.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  3. Get a set of default Download Preplanned Offline Map Parameters. These parameters describe how the owner of the web map configured the offline map to be downloaded by default. You can modify the parameters to fine tune the downloaded offline map.

    Options include whether to include the basemap or use one that has already been downloaded, whether to opt into scheduled updates, and whether to disable offline editing even if the source web map supports editing. To learn more, see Optimize an offline map.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  4. Use the parameters to create a Download Preplanned Offline Map Job to download the offline map, specifying the download location.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  5. Start the job and wait for it to complete, returning a Map, which is ready for use.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble

Access

When an offline map is first downloaded, the ArcGIS Runtime API returns an API reference to the offline map stored on the device. To access a previously downloaded offline map, an offline application must create this API reference directly.

How to access an offline map

  1. Create a Mobile Map Package referencing the downloaded offline map.
    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  2. Load the mobile map package and read the Map from it.
    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble

Edit

If a feature layer or non-spatial table in the source web map supports editing, the same layer or table can be edited in the offline map. You can create, update and delete data as offline edits. See Editing offline data.

When a network connection is available, offline edits can be synchronized with the source web map.

Synchronize

You can updated the data contents of an offline map by synchronizing the offline map with its source web map. Offline feature data edits can be posted to the source web map, and any edits to the source web map's feature data contents can be downloaded and applied to the offline map, bringing the source web map contents and the offline map contents into alignment. Edits to features, non-spatial data, related data, and attachments can be synchronized. A network connection is required.

You can choose whether to:

  • Upload edits from the offline map to the source web map.
  • Download edits from the source web map to the offline map.
  • Both upload and download (bidirectional sync). This is the default.

You can control the synchronization direction (upload/download/bidirectional) for the entire offline map or on a layer-by-layer basis.

How to synchronize an offline map

  1. Create an Offline Map Sync Task referencing the offline map.
    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  2. Use the Offline Map Sync Task to get default Offline Map Sync Parameters. Optionally modify these (for example, by specifying a synchronization direction).
    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  3. Use the parameters to create an Offline Map Sync Job.
    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  4. Start the Offline Map Sync Job. If it completes without errors, the offline map and web map will be in sync.
    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble

Synchronization conflicts

If there are attribute or geometry level conflicts on a feature during synchronization, the most recently synchronized edit will be applied. For example, if both user A and user B edit the same feature while offline, if user A synchronizes their edits first, then user B synchronizes their edits, the updated feature will represent the edits made by user B.

Synchronization errors

Synchronization errors typically occur because of network connectivity issues during the sync process. The synchronization mechanism is robust to these types of errors, however, and they can be resolved by synchronizing again when a reliable network connection becomes available.

If you encounter a synchronization error, you can safely continue to view and edit the offline data until you are able to synchronize again.

Unregister

When your offline application no longer needs an offline map, it is recommended that you unregister any feature data contained in the offline map You do not need to unregister if the offline map is read-only.. You do this using the feature services referenced in the source web map. Unregistering will delete edit-tracking metadata that is stored in each feature service, so you will no longer be able to synchronize edits with the source web map. To unregister, a network connection is required.

Once unregistered, you can delete the offline map from local storage (you must release any API references to the offline map first).

When you unregister data from an offline map, you are sending a sync ID to each feature service referenced by the source web map. If you need disk space immediately but do not have a network connection to make the unregister request, you can take note of the feature service URLs, and the sync IDs, delete the offline map, and unregister the data later.

While it is recommended that you unregister offline map feature data from the source feature service(s), if you are unable to (for example, if a device gets lost or destroyed) the impact on the source feature service's performance is negligible. If you build up a large number of stale registration entries, you should consider purging the old ones, which can be done by an administrator.

You can keep using an offline map once it has been unregistered, but you will no longer be able to synchronize edits with the source web map.

How to unregister an offline map

To unregister an offline map, you first determine the .geodatabase files that the offline map references. Each of these represents offline data from a single feature service. You can then unregister each .geodatabase file from its source feature service.

  1. Iterate over the offline map's operationalLayers and tables collections to list all the Geodatabase Feature Tables referenced by the offline map.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  2. Build a set of Geodatabases that contain the Geodatabase Feature Tables, and read the geodatabase Sync IDs and feature service URLs.

    Only Geodatabases that are registered with a feature service will have a Sync ID. Geodatabases that are downloaded as read only are not registered with the source feature service.
    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble
  3. Create a Geodatabase Sync Task for each feature service and use the Sync ID to unregister the geodatabase.

    ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                                                                     
    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
    // Preamble
    var webMap = new Map(new Uri("https://www.arcgis.com/home/item.html?id=acc027394bc84c2fb04d1ed317aac674"));
    
    var offlineMapTask = await OfflineMapTask.CreateAsync(webMap);
    
    IReadOnlyList<PreplannedMapArea> preplannedAreas = await offlineMapTask.GetPreplannedMapAreasAsync();
    PreplannedMapArea selectedArea = preplannedAreas.First();
    
    DownloadPreplannedOfflineMapParameters preplannedParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(selectedArea);
    
    DownloadPreplannedOfflineMapJob downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(preplannedParameters, "path\\to\\download\\location");
    
    DownloadPreplannedOfflineMapResult downloadResult = await downloadJob.GetResultAsync();
    
    if (downloadResult?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
        await MainMapView.SetViewpointAsync(offlineMap.InitialViewpoint);
    }
    
    // Note: current viewpoint is used for convenience; Use a target area that makes sense for your app
    var areaOfInterest = MainMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;
    GenerateOfflineMapParameters onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
    
    onDemandParameters = await offlineMapTask.CreateDefaultGenerateOfflineMapParametersAsync(
        areaOfInterest: areaOfInterest,
        maxScale: 100_000,
        minScale: 10_000_000);
    
    GenerateOfflineMapJob generateJob = offlineMapTask.GenerateOfflineMap(onDemandParameters, "path\\to\\download\\location");
    
    GenerateOfflineMapResult generateResult = await generateJob.GetResultAsync();
    offlineMap = generateResult.OfflineMap;
    
    OfflineMapSyncTask offlineMapSyncTask = await OfflineMapSyncTask.CreateAsync(offlineMap);
    
    OfflineMapSyncParameters syncParameters = await offlineMapSyncTask.CreateDefaultOfflineMapSyncParametersAsync();
    
    // Modify parameters if necessary, e.g. sync direction
    
    OfflineMapSyncJob syncJob = offlineMapSyncTask.SyncOfflineMap(syncParameters);
    
    OfflineMapSyncResult syncResult = await syncJob.GetResultAsync();
    
    
    
    var mobileMapPackage = await MobileMapPackage.OpenAsync("path\\to\\downloaded\\location");
    
    await mobileMapPackage.LoadAsync();
    MainMapView.Map = mobileMapPackage.Maps.First();
    
    var layerTables = offlineMap.OperationalLayers.OfType<FeatureLayer>()
        .Select(layer => layer.FeatureTable).OfType<GeodatabaseFeatureTable>()
        .Union(offlineMap.Tables.OfType<GeodatabaseFeatureTable>());
    
    var uriToSyncId = layerTables.Where(table => table.Geodatabase.IsSyncEnabled() && table.Geodatabase.SyncId != null)
                    .Select(table => new KeyValuePair<Uri, Guid>(table.Geodatabase.Source, table.Geodatabase.SyncId));
    
    var tasks = uriToSyncId.Select((kvp) => {
        return GeodatabaseSyncTask.CreateAsync(kvp.Key)
                .ContinueWith(task => task.Result.UnregisterGeodatabaseAsync(kvp.Value));
    });
    await Task.WhenAll(tasks);
    
    // Postamble

Examples

Download a custom area of a web map

Use the Offline Map Task to download a custom area of a web map. Create and start a Generate Offline Map Job for a specific area of the web map. When the job completes, you will receive a Map object that you can display in a Map View:

  1. Create an Offline Map Task using an offline enabled web map.

  2. Specify a geographic area to download.

  3. Create a Generate Offline Map Job.

  4. Start the job and wait for it to complete.

  5. Set the Map View's map to the offline map.

ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//using Esri.ArcGISRuntime.Mapping;
//using Esri.ArcGISRuntime.Portal;
//using Esri.ArcGISRuntime.Tasks.Offline;

var portal = await ArcGISPortal.CreateAsync();
var portalItem = await PortalItem.CreateAsync(portal, "acc027394bc84c2fb04d1ed317aac674");
var map = new Map(portalItem);

OfflineMapTask offlineTask = await OfflineMapTask.CreateAsync(map);

GenerateOfflineMapParameters generateParameters =
    await offlineTask.CreateDefaultGenerateOfflineMapParametersAsync(areaOfInterest);

GenerateOfflineMapJob job = offlineTask.GenerateOfflineMap(generateParameters, "path\\to\\map\\directory");

// Optionally listen for progress changes
//job.ProgressChanged += Job_ProgressChanged;

GenerateOfflineMapResult result = await job.GetResultAsync();

if (result.OfflineMap is Map offlineMap)
{
    MainMapView.Map = offlineMap;
}

Download a preplanned area of a web map

Use the Offline Map Task to list the preplanned offline maps available for a web map. Create and start a Download Preplanned Offline Map Job. When the job completes, you will receive a Map object that you can display in a Map View:

  1. Create an Offline Map Task using an offline enabled web map.

  2. Request a list of available offline maps.

  3. Select one of the offline maps to download.

  4. Create a Download Preplanned Offline Map Job.

  5. Start the job and wait for it to complete.

  6. Set the Map View's map to the offline map.

ArcGIS .NET APIArcGIS Android APIArcGIS iOS APIArcGIS Java APIArcGIS Qt API (C++)ArcGIS Qt API (QML)
                           
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
//using Esri.ArcGISRuntime.Mapping;
//using Esri.ArcGISRuntime.Portal;
//using Esri.ArcGISRuntime.Tasks.Offline;

var portal = await ArcGISPortal.CreateAsync();
var portalItem = await PortalItem.CreateAsync(portal, "acc027394bc84c2fb04d1ed317aac674");
var map = new Map(portalItem);

OfflineMapTask offlineMapTask = await OfflineMapTask.CreateAsync(map);

IReadOnlyList<PreplannedMapArea> availableAreas = await offlineMapTask.GetPreplannedMapAreasAsync();

if (availableAreas?.FirstOrDefault() is PreplannedMapArea area)
{
    DownloadPreplannedOfflineMapParameters downloadParameters = await offlineMapTask.CreateDefaultDownloadPreplannedOfflineMapParametersAsync(area);

    // Modify parameters if necessary

    DownloadPreplannedOfflineMapJob job = offlineMapTask.DownloadPreplannedOfflineMap(downloadParameters, "path\\to\\map\\directory");

    DownloadPreplannedOfflineMapResult result = await job.GetResultAsync();

    if (result?.OfflineMap is Map offlineMap)
    {
        MainMapView.Map = offlineMap;
    }
}

Tutorials

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