Skip to content

Instantly share code, notes, and snippets.

@anstrevor
Last active Jul 29, 2016
Embed
What would you like to do?
.NET Xamarin gist to download ArcGIS basemap tiles for offline use.
using Esri.ArcGISRuntime.ArcGISServices;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Tasks.Offline;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Diagnostics;
namespace JcaFormsMapsShared.ArcGisRendering
{
/// <summary>
/// taken from https://earlyadopter.esri.com/project/forum/thread.html?cap=4dbe143a73cc4e629ba34dde5090139c&forid=%7bc75abab4-201a-449a-bdc9-f2ed98b6e922%7d&topid=%7bc16c350d-215e-4bec-8570-2ef904014774%7d&tp=1&to=asc&ts=last&tl=15&tv=desc#%7bB864C33D-BFF1-4F55-9B83-FD87BACB9670%7d
/// Download basemap tile caches, can only download a max of 100,000 tiles.
/// Right now the min/max scale of the download is fixed to provide a high enough level of detail,
/// without an overly large file size or download duration.
/// </summary>
public class OffLineMap
{
// Default export paths
private static string defaultFilePath_ = "/storage/emulated/0/SmartMaps/";
private static string defaultFileName_ = "tile-cache.tpk";
// ArgGIS map server for imagery data
private const string ONLINE_BASEMAP_URL = "http://tiledbasemaps.arcgis.com/arcgis/rest/services/World_Imagery/MapServer";
// Folder location for export
public string OfflineMapLocation { get; set; }
// File name for export
public string OfflineMapName { get; set; }
// Current map view and extent set by the user. This view
// will be used to export chosen tiles.
public Envelope MapViewEnvelope { get; set; }
// Export tile operation
private static ExportTileCacheTask exportTileTask_ = null;
// Tile export parameters
private static ExportTileCacheParameters tileParameters_ = null;
/// <summary>
/// Tile size estimate.
/// </summary>
public struct TileSizeEstimate
{
public int TileCount;
public double TileSize;
public string ErrorMessage;
};
/// <summary>
/// Download basemap tiles from argGIS server for offline use. The tiles can be imagery, topographic etc.
/// Downloading the tiles requires an Esri account with sufficient privileges to download tiles. See here:
/// http://www.arcgis.com/home/item.html?id=226d23f076da478bba4589e7eae95952 for more information.
/// </summary>
public async void DownLoadOffLineMap()
{
try
{
// Establish Esri login token
var options = new Esri.ArcGISRuntime.Security.GenerateTokenOptions()
{
Referer = new Uri(ONLINE_BASEMAP_URL)
};
// Use an Esri account to generate a security credential. Account must be a developer account
// have access to map export services.
var cred = await Esri.ArcGISRuntime.Security.AuthenticationManager.Current.GenerateCredentialAsync(
new Uri("https://www.arcgis.com/sharing/rest/generatetoken"),
<Username>, <password>,
options);
//Check the credential and add to the identity manager
if (cred != null)
Esri.ArcGISRuntime.Security.AuthenticationManager.Current.AddCredential(cred);
// Get URI from server URL
Uri server = new Uri(ONLINE_BASEMAP_URL);
//Create the service for the export, this requires the credential
var tileService = await ArcGISMapServiceInfo.CreateAsync(server, cred);
// Create the export task
exportTileTask_ = new ExportTileCacheTask(server);
// Get map scales from tile service. In this case take the smallest scales available
// (smallest map area) that we can reasonably export.
int arrayLength = tileService.TileInfo.LevelsOfDetail.ToArray().Length;
double minScale = tileService.TileInfo.LevelsOfDetail.ToArray()[arrayLength - 10].Scale;
double maxScale = tileService.TileInfo.LevelsOfDetail.ToArray()[arrayLength - 5].Scale;
// Get export parameters, using the current map viewpoint and the chosen scales.
tileParameters_ = await exportTileTask_.CreateExportTileCacheParametersAsync(MapViewEnvelope,
minScale,
maxScale);
// Download tile cache
var cache = await downloadTileCache(OfflineMapLocation, OfflineMapName, MapViewEnvelope);
}
catch (Exception ex)
{
var errorMessage = ex.Message;
Debug.WriteLine("An error has occurred while exporting tiles");
}
}
/// <summary>
/// Download basemap tile cache from online source. Download is limited to 100,000 tiles.
/// </summary>
/// <param name="mapLocation">Folder location for the tile cache</param>
/// <param name="mapName">Out put name of the tile cache</param>
/// <param name="mapExtent">Current view extent of the map to be exported</param>
/// <returns>Tile cache task that contains the status of the exported tile cache</returns>
public static async Task<TileCache> downloadTileCache(string mapLocation, string mapName, Envelope mapExtent)
{
// Build and check output file path. Use default if not specified
string exportPath = defaultFilePath_ + defaultFileName_;
if (mapLocation == null || mapName == null)
{
mapLocation = defaultFilePath_;
mapName = defaultFileName_;
}
exportPath = System.IO.Path.Combine(mapLocation, mapName);
// Try and create the directory if it doesn't exist.
if (!System.IO.Directory.Exists(exportPath))
{
try
{
System.IO.Directory.CreateDirectory(mapLocation);
}
catch (Exception ex)
{
Debug.WriteLine("Error, could not create directory for tile cache");
return null;
}
}
// Download tile cache and export to file
Job<TileCache> exportJob = exportTileTask_.ExportTileCache(tileParameters_, exportPath);
//Create the handler for status updates
exportJob.JobChanged += (object sender, EventArgs evt) => {
Debug.WriteLine("[EXPORT STATUS MESSAGE] " + exportJob.Status.ToString());
};
//Get the result
TileCache cache = await exportJob.GetResultAsync();
await cache.LoadAsync();
//Check the result
if (cache == null)
{
Debug.WriteLine("Tile cache export failed");
}
else
{
Debug.WriteLine("Tile cache export success!");
}
return cache;
}
} // class
} // namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment