download basemap

8116
9
06-28-2021 11:02 AM
johnmarker
New Contributor III

Hello,

Is it possible to take just a base map, like the topographic map, offline? There do not need to be any other custom layers. This should eliminate having to create a full tile package hopefully. Is there any resource that has a list of downloadable base maps for offline use?

0 Kudos
9 Replies
LukeSmallwood
Esri Contributor

Hi @johnmarker - yes you can take either raster or vector basemaps offline using Runtime.

 

The ExportTileTileCache task allows you to export raster tiles from suitable online services as a tile package (.tpk or .tpkx) - see this sample.

The ExportVectorTilesTask lets you take vector basemap tiles offline as a vector tile package (.vtpk). You can also export styles to change the appearance of your offline vector basemap.

 

Esri has a number of services that support exporting tiles - for example see this AGOL group. Just a note that you will need to sign (or use an API key) to export from these esri services.

 

Hope that helps!

johnmarker
New Contributor III

@LukeSmallwood Thanks for sharing that info.

It looks like they offer a lot of ways to download a basemap for use offline, but as i have been looking around it looks like that most the time these offline maps remain on the local server. Is there a way to possibly download the basemap directly to the local file system of the computer? The AGOL group link you attached had many maps available for export but it seemed they were for export to other Esri/ArcGIS products like ArcGIS pro rather than being able to download them as a tile or raster file. Is it possible to do this?

0 Kudos
LukeSmallwood
Esri Contributor

Hi @johnmarker - Could you give some more details of the workflow you're building? I'm not sure exactly what you mean by the "local server". Typically a Runtime app that wants to take a basemap offline might do something like:

1 - Load the online service (e.g. one of those in the group I linked above) when you have a connection (e.g. in the office)

2 - Run either the ExportTileCacheTask or ExportVectorTilesTask to download the tile package to the local device (the local file system of the computer)

3 - Use the tile package in a new layer which won't need a network connection

 

You can either do all 3 steps in a single app, or have one which takes the data offline and then another app that simply consumes it. You can also share a tile package with Runtime apps on other devices if needed.

 

Does that make sense?

johnmarker
New Contributor III

@LukeSmallwood I used that sample you shared a link to for ExportTileCache. It uses the default World_Street_Map and contains a link to it. I have tread looking around and different URL's, but I'm not sure hw to export different maps (like world topographic). The AGOL link shows me a bunch of different maps available for export but I'm not sure where to go/what URL to use for those maps in the program in order to export them.

0 Kudos
LukeSmallwood
Esri Contributor

Hi @johnmarker  - take a look at the basemaps in this group. If you log in or use an API key you should be able to export tiles from those. For example, world topographic would use the url: https://tiledbasemaps.arcgis.com/arcgis/rest/services/World_Topo_Map/MapServer

However, for most basemaps other than imagery, we'd generally recommend that you consider using the ExportVectorTilesTask to export a vector tiled basemap. You can export the vector equivalent of the world topographic map with this url: https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_Export_v2/VectorTileServer

johnmarker
New Contributor III

@LukeSmallwood okay that makes a lot more sense. Thanks for all of your explanations.

I know it is probably very similar, but is there an example provided for using the ExportVectorTiles?

0 Kudos
LukeSmallwood
Esri Contributor

Hi @johnmarker - I'm sorry I can't find an example of using that code right now but you are correct the workflow is pretty similar to the other sample.

- 1. Create a new instance of the ExportVectorTilesTask. You have a coupe of options here, you can either use a PortalItem (that can include a style for the tiles) or a URL (e.g. to the underlying service)

- 2. Create parameters for the export - a good idea is to use the create default parameters method https://developers.arcgis.com/net/wpf/api-reference/html/M_Esri_ArcGISRuntime_Tasks_Offline_ExportVe... which will create parameters with sensible defaults

- 3. Adjust the parameters to suit your workflow (e.g. set the max scale to control how much detail is included)

- 4. Create an ExportVectorTilesJob by passing the parameters to the task. You can choose to download the tiles (as a .vtpk) and or a style (as a an itemresourcecache)

- 5. Run the job to completion and get the tiles/style from the result

- 6. You can create an ArcGISVectorTiledLayer using the path to the tiles (the .vtpk) and optionally to the style.

 

Hope that helps,

 

Luke

 

0 Kudos
johnmarker
New Contributor III

@LukeSmallwood 

So I'm occasionally getting an error with my ExportVectorTiles. Depending one where I try to export from I will get an error like this...

johnmarker_0-1625257141972.png

 

 

namespace ArcGISRuntime.WPF.Samples.ExportTiles
{
    public partial class ExportTiles
    {
        // URL to the service tiles will be exported from.
        private Uri _serviceUri = new Uri("https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_Export_v2/VectorTileServer");

        public ExportTiles()
        {
            InitializeComponent();
            // Call a function to set up the map.
            Initialize();
        }

        private async void Initialize()
        {
            try
            {
                // Create the tile layer.
                ArcGISVectorTiledLayer myLayer = new ArcGISVectorTiledLayer(_serviceUri);

                // Load the layer.
                await myLayer.LoadAsync();

                // Create the basemap with the layer.
                Map myMap = new Map(new Basemap(myLayer))
                {

                    // Set the min and max scale - export task fails if the scale is too big or small.
                    MaxScale = 5000000,
                    MinScale = 10000000
                };

                // Assign the map to the mapview.
                MyMapView.Map = myMap;

                // Create a new symbol for the extent graphic.
                //     This is the red box that visualizes the extent for which tiles will be exported.
                SimpleLineSymbol myExtentSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.Red, 2);

                // Create graphics overlay for the extent graphic and apply a renderer.
                GraphicsOverlay extentOverlay = new GraphicsOverlay
                {
                    Renderer = new SimpleRenderer(myExtentSymbol)
                };

                // Add the overlay to the view.
                MyMapView.GraphicsOverlays.Add(extentOverlay);

                // Subscribe to changes in the mapview's viewpoint so the preview box can be kept in position.
                MyMapView.ViewpointChanged += MyMapView_ViewpointChanged;

                // Update the graphic - needed in case the user decides not to interact before pressing the button.
                UpdateMapExtentGraphic();

                // Enable the export button.
                MyExportButton.IsEnabled = true;

                // Set viewpoint of the map.
                MyMapView.SetViewpoint(new Viewpoint(-4.853791, 140.983598, myMap.MinScale));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void MyMapView_ViewpointChanged(object sender, EventArgs e)
        {
            UpdateMapExtentGraphic();
        }

        private void UpdateMapExtentGraphic()
        {
            // Return if mapview is null.
            if (MyMapView == null) { return; }

            // Get the new viewpoint.
            Viewpoint myViewPoint = MyMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry);

            // Return if viewpoint is null.
            if (myViewPoint == null) { return; }

            // Get the updated extent for the new viewpoint.
            Envelope extent = myViewPoint.TargetGeometry as Envelope;

            // Return if extent is null.
            if (extent == null) { return; }

            // Create an envelope that is a bit smaller than the extent.
            EnvelopeBuilder envelopeBldr = new EnvelopeBuilder(extent);
            envelopeBldr.Expand(0.80);

            // Get the (only) graphics overlay in the map view.
            GraphicsOverlay extentOverlay = MyMapView.GraphicsOverlays.FirstOrDefault();

            // Return if the extent overlay is null.
            if (extentOverlay == null) { return; }

            // Get the extent graphic.
            Graphic extentGraphic = extentOverlay.Graphics.FirstOrDefault();

            // Create the extent graphic and add it to the overlay if it doesn't exist.
            if (extentGraphic == null)
            {
                extentGraphic = new Graphic(envelopeBldr.ToGeometry());
                extentOverlay.Graphics.Add(extentGraphic);
            }
            else
            {
                // Otherwise, simply update the graphic's geometry.
                extentGraphic.Geometry = envelopeBldr.ToGeometry();
            }
        }

        private async Task StartExport()
        {
            // Get the (only) graphics overlay in the map view.
            GraphicsOverlay extentOverlay = MyMapView.GraphicsOverlays.First();

            // Get the area selection graphic's extent.
            Graphic extentGraphic = extentOverlay.Graphics.First();

            // Create the task.
            ExportVectorTilesTask exportTask = await ExportVectorTilesTask.CreateAsync(_serviceUri);
            ExportVectorTilesParameters parameters = await exportTask.CreateDefaultExportVectorTilesParametersAsync(extentGraphic.Geometry, 100);

            // Get the tile cache path.
            string tilePath = Path.Combine(Environment.ExpandEnvironmentVariables("%TEMP%"), Path.GetTempFileName() + ".vtpk");
            //string tilePath = Path.Combine("C:/Development/Map_Tiles", fileName.Text + ".vtpk");

            // Create the export job.
              ExportVectorTilesJob job = exportTask.ExportVectorTiles(parameters, tilePath);

            // Start the export job.
            job.Start();

            // Wait for the job to complete.
            ExportVectorTilesResult resultTileCache = await job.GetResultAsync();
             
            // Do the rest of the work.
            await HandleExportCompleted(job, resultTileCache);
        }

        private async Task HandleExportCompleted(ExportVectorTilesJob job, ExportVectorTilesResult cache)
        {
            if (job.Status == Esri.ArcGISRuntime.Tasks.JobStatus.Succeeded)
            {
                // Show the exported tiles on the preview map.
                await UpdatePreviewMap(cache);

                // Show the preview window.
                MyPreviewMapView.Visibility = Visibility.Visible;

                // Show the 'close preview' button.
                MyClosePreviewButton.Visibility = Visibility.Visible;

                // Hide the 'export tiles' button.
                MyExportButton.Visibility = Visibility.Collapsed;

                // Hide the progress bar.
                MyProgressBar.Visibility = Visibility.Collapsed;
            }
            else if (job.Status == Esri.ArcGISRuntime.Tasks.JobStatus.Failed)
            {
                // Notify the user.
                MessageBox.Show("Job failed");

                // Hide the progress bar.
                MyProgressBar.Visibility = Visibility.Collapsed;
            }
        }

        private async Task UpdatePreviewMap(ExportVectorTilesResult cache)
        {
            VectorTileCache vc = cache.VectorTileCache;
            // Load the cache.
            await vc.LoadAsync();

            // Create a tile layer from the tile cache.
            ArcGISVectorTiledLayer myLayer = new ArcGISVectorTiledLayer(vc);
            

            // Show the layer in a new map.
            MyPreviewMapView.Map = new Map(new Basemap(myLayer));
        }

        private async void MyExportButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Show the progress bar.
                MyProgressBar.Visibility = Visibility.Visible;

                // Hide the preview window.
                MyPreviewMapView.Visibility = Visibility.Collapsed;

                // Hide the 'close preview' button.
                MyClosePreviewButton.Visibility = Visibility.Collapsed;

                // Show the 'export tiles' button.
                MyExportButton.Visibility = Visibility.Visible;

                // Disable the 'export tiles' button.
                MyExportButton.IsEnabled = false;

                // Start the export.
                await StartExport();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void ClosePreview_Click(object sender, RoutedEventArgs e)
        {
            // Hide the preview map.
            MyPreviewMapView.Visibility = Visibility.Collapsed;

            // Hide the close preview button.
            MyClosePreviewButton.Visibility = Visibility.Collapsed;

            // Show the 'export tiles' button.
            MyExportButton.Visibility = Visibility.Visible;

            // Re-enable the export button.
            MyExportButton.IsEnabled = true;
        }
    }
}
0 Kudos
LukeSmallwood
Esri Contributor

Hi @johnmarker thanks for sharing your code. From the error message it looks as though you are trying to export tiles from an area with no data (e.g. away from land) - is that correct?

0 Kudos