Learn how to download and display an offline map for a user-defined geographical area of a web map.
Offline maps allow users to continue working when network connectivity is poor or lost. If a web map is enabled for offline use, a user can request that ArcGIS generates an offline map for a specified geographic area of interest.
In this tutorial, you will download an offline map for an area of interest from the web map of the
stormwater network within Naperville, IL, USA
. You can then use this offline map without a network connection.Prerequisites
Before starting this tutorial:
-
You need an ArcGIS Location Platform or ArcGIS Online account.
-
Your system meets the system requirements.
-
The ArcGIS Runtime API for Qt is installed.
Steps
Get the web map item ID
You can use ArcGIS tools to create and view web maps. Use the Map Viewer to identify the web map item ID. This item ID will be used later in the tutorial.
- Go to the Naperville water network
in the Map Viewer in ArcGIS Online. This web map displays
stormwater network within Naperville, IL, USA
. - Make a note of the item ID at the end of the browser's URL. The item ID should be
acc027394bc84c2fb04d1ed317aac674
.
Create a new ArcGIS Runtime Qt Creator Project
-
Launch Qt Creator and create a new project. Under Choose a Template, select Qt Quick C++ app project for the latest version of ArcGIS Runtime installed.
-
Name your project Display_an_offline_map.
-
Accept all defaults. At the Define Project Details window, leave the ArcGIS Online Basemap selection as is. Complete the project creation.
Get an access token
You need an access token to use the location services used in this tutorial.
-
Go to the Create an API key tutorial to obtain an access token.
-
Ensure that the following privilege is enabled: Location services > Basemaps > Basemap styles service.
-
Copy the access token as it will be used in the next step.
To learn more about other ways to get an access token, go to Types of authentication.
Set your API key
-
In the Projects window, in the Sources folder, open the main.cpp file. Modify the code to set the API key to the access token. Save and close the file.
main.cppUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. // 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token. const QString accessToken = QString("");
Display the web map
You can display a web map using the web map's item ID. Create a map from the web map portal item, and display it in your app.
-
In Projects, double-click Headers > Display_an_offline_map.h to open the file. Add the following classes to the header file.
Display_an_offline_map.hUse dark colors for code blocks 19 20 21 22 23Add line. Add line. namespace ArcGISRuntime { class Map; class MapQuickView; class PortalItem; class OfflineMapTask;
-
Add the
generate
function declaration and member variablesMap By Extent() m
and_portal Item m
. Save and close the file._offline Map Task Display_an_offline_map.hUse dark colors for code blocks 45 46 47 48 50 51 52 53Add line. Add line. Add line. private: Esri::ArcGISRuntime::MapQuickView* mapView() const; void setMapView(Esri::ArcGISRuntime::MapQuickView* mapView); void generateMapByExtent(); Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::PortalItem* m_portalItem = nullptr; Esri::ArcGISRuntime::OfflineMapTask* m_offlineMapTask = nullptr;
-
In Projects, double-click Sources > Display_an_offline_map.cpp to open the file. Remove the
#include
statements for Basemap and QUrl.Display_an_offline_map.cppUse dark colors for code blocks 2 4 5 6 7 9 10Remove line Remove line #include "Basemap.h" #include "Map.h" #include "MapQuickView.h" #include <QUrl> using namespace Esri::ArcGISRuntime;
-
Add the following
#include
statements.Display_an_offline_map.cppUse dark colors for code blocks 13 14 15 16Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. #include "Display_an_offline_map.h" #include "Map.h" #include "MapQuickView.h" #include "OfflineMapTask.h" #include "Envelope.h" #include "EnvelopeBuilder.h" #include "GenerateOfflineMapJob.h" #include "GraphicsOverlay.h" #include "SimpleFillSymbol.h" #include "SimpleLineSymbol.h" #include <QDir>
-
Remove the Map from the constructor and also delete the comma after
Q
.Object(parent) Display_an_offline_map.cppUse dark colors for code blocks 11 12 13Remove line Display_an_offline_map::Display_an_offline_map(QObject* parent /* = nullptr */): QObject(parent), m_map(new Map(BasemapStyle::ArcGISStreets, this))
-
Instantiate a Portal and from that, a PortalItem using the web map item ID for the Naperville water network. Use that to create a new Map. Then create a new OfflineMapTask referencing that portal item.
Display_an_offline_map.cppUse dark colors for code blocks 28 29 30 31Add line. Add line. Add line. Add line. Display_an_offline_map::Display_an_offline_map(QObject* parent /* = nullptr */): QObject(parent) { Portal* portal = new Portal(false, this); m_portalItem = new PortalItem(portal, "acc027394bc84c2fb04d1ed317aac674", this); m_map = new Map(m_portalItem, this); m_offlineMapTask = new OfflineMapTask(m_portalItem, this);
-
Press Ctrl + R to run the app.
You should see a map of the stormwater network within Naperville, IL, USA. Use the mouse to drag, scroll, and double-click the map view to explore the map.
Specify the area of the web map to take offline
-
Add the call to
generate
. (The next step implements this function.)Map By Extent Display_an_offline_map.cppUse dark colors for code blocks 48 49 50 51 52 53 54 55 56 57 58Add line. // Set the view (created in QML) void Display_an_offline_map::setMapView(MapQuickView* mapView) { if (!mapView || mapView == m_mapView) { return; } m_mapView = mapView; m_mapView->setMap(m_map); generateMapByExtent();
-
Begin to implement the
generate
function. Use EnvelopeBuilder to set four parameters and then callMap By Extent() to
to create the envelope.Geometry Display_an_offline_map.cppUse dark colors for code blocks 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. // Set the view (created in QML) void Display_an_offline_map::setMapView(MapQuickView* mapView) { if (!mapView || mapView == m_mapView) { return; } m_mapView = mapView; m_mapView->setMap(m_map); generateMapByExtent(); emit mapViewChanged(); } void Display_an_offline_map::generateMapByExtent() { // Create envelope to define area of interest EnvelopeBuilder* envelopeBuilder = new EnvelopeBuilder(SpatialReference::wgs84(), this); envelopeBuilder->setXMin(-88.1526); envelopeBuilder->setXMax(-88.1490); envelopeBuilder->setYMin(41.7694); envelopeBuilder->setYMax(41.7714); Envelope offlineArea = envelopeBuilder->toGeometry(); // generate the offline map parameters m_offlineMapTask->createDefaultGenerateOfflineMapParameters(offlineArea); }
-
Add a graphic to
generate
to show the area you will take offline.Map By Extent() Create a Graphic using the envelope, SimpleFillSymbol and SimpleLineSymbol. From this create a GraphicsOverlay and add it to the mapView.
Display_an_offline_map.cppUse dark colors for code blocks 64 65 66 67 68 69 70 71 72 73 74 75 82 83 84 85Add line. Add line. Add line. Add line. Add line. Add line. void Display_an_offline_map::generateMapByExtent() { // Create envelope to define area of interest EnvelopeBuilder* envelopeBuilder = new EnvelopeBuilder(SpatialReference::wgs84(), this); envelopeBuilder->setXMin(-88.1526); envelopeBuilder->setXMax(-88.1490); envelopeBuilder->setYMin(41.7694); envelopeBuilder->setYMax(41.7714); Envelope offlineArea = envelopeBuilder->toGeometry(); Graphic* box = new Graphic(offlineArea, new SimpleFillSymbol(SimpleFillSymbolStyle::Solid, Qt::transparent, new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, Qt::red, 3, this), this), this); GraphicsOverlay* boxOverlay = new GraphicsOverlay(this); boxOverlay->graphics()->append(box); // add graphics overlay to the map view m_mapView->graphicsOverlays()->append(boxOverlay); // generate the offline map parameters m_offlineMapTask->createDefaultGenerateOfflineMapParameters(offlineArea); }
-
Press Ctrl + R to run the app.
You should see a red outline on the stormwater network within Naperville, IL, USA. This indicates the area of the web map that you are going to take offline.
Download and display the offline map
-
Add the following connect function that creates a GenerateOfflineMapJob. This executes after
create
is issued. Store the resulting offline mobile map package (MMPK) at a desired local folder. Don't add a closing brace yet.Default Generate Offline Map Parameters Completed Display_an_offline_map.cppUse dark colors for code blocks 76 77 78 79 80 81 82Add line. Add line. Add line. Add line. Add line. Add line. Add line. Graphic* box = new Graphic(offlineArea, new SimpleFillSymbol(SimpleFillSymbolStyle::Solid, Qt::transparent, new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, Qt::red, 3, this), this), this); GraphicsOverlay* boxOverlay = new GraphicsOverlay(this); boxOverlay->graphics()->append(box); // add graphics overlay to the map view m_mapView->graphicsOverlays()->append(boxOverlay); // use connect to run generate offline map job after offline map parameters are created. Store the resulting mmpk in the desired path connect(m_offlineMapTask, &OfflineMapTask::createDefaultGenerateOfflineMapParametersCompleted, this, [this](QUuid, const GenerateOfflineMapParameters& params) { const QString outputPath = QDir::homePath() + "/ArcGIS/Runtime/Data/offlinemap.mmpk"; GenerateOfflineMapJob* generateJob = m_offlineMapTask->generateOfflineMap(params, outputPath); if (!generateJob) return;
-
Add another
connect
that monitors the status of the GenerateOfflineMapJob and returns changes to status.Display_an_offline_map.cppUse dark colors for code blocks 83 84 85 86 87 88 89 90Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. // use connect to run generate offline map job after offline map parameters are created. Store the resulting mmpk in the desired path connect(m_offlineMapTask, &OfflineMapTask::createDefaultGenerateOfflineMapParametersCompleted, this, [this](QUuid, const GenerateOfflineMapParameters& params) { const QString outputPath = QDir::homePath() + "/ArcGIS/Runtime/Data/offlinemap.mmpk"; GenerateOfflineMapJob* generateJob = m_offlineMapTask->generateOfflineMap(params, outputPath); if (!generateJob) return; // use connect to monitor job status for changes and return job status connect(generateJob, &GenerateOfflineMapJob::statusChanged, this, [this, generateJob]() { if (generateJob->jobStatus() == JobStatus::Succeeded) { qDebug() << "generating offline map"; m_mapView->setMap(generateJob->result()->offlineMap(this)); qDebug() << (generateJob->error().isEmpty() ? "No errors" : (generateJob->error().message() + generateJob->error().additionalMessage())); } else if (generateJob->jobStatus() == JobStatus::Failed) { qWarning() << generateJob->error().message() << generateJob->error().additionalMessage(); } });
-
Add a final
connect
(optional) that displays progress of the GenerateOfflineMapJob as the job executes. Add the final codegenerate
and the final closing code (required) to complete the first connect function.Job- >start() Display_an_offline_map.cppUse dark colors for code blocks 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. // use connect to monitor job status for changes and return job status connect(generateJob, &GenerateOfflineMapJob::statusChanged, this, [this, generateJob]() { if (generateJob->jobStatus() == JobStatus::Succeeded) { qDebug() << "generating offline map"; m_mapView->setMap(generateJob->result()->offlineMap(this)); qDebug() << (generateJob->error().isEmpty() ? "No errors" : (generateJob->error().message() + generateJob->error().additionalMessage())); } else if (generateJob->jobStatus() == JobStatus::Failed) { qWarning() << generateJob->error().message() << generateJob->error().additionalMessage(); } }); // use connect to monitor job progress changes and return progress return job progress connect(generateJob, &GenerateOfflineMapJob::progressChanged, this, [generateJob]() { qDebug() << "Job status:" << generateJob->progress() << "%"; }); generateJob->start(); });
-
Press Ctrl + R to run the app.
Initially, you should see the map of the stormwater network for Naperville, IL, USA, with a red outline as before. At the Application Output tab in Creator, the Job status
percentage should increment up to 100%, and then you should see the offline map for the specified area of the stormwater network for Naperville, IL, USA. Remove your network connection and you will still be able to use the mouse to drag, scroll, and double-click the map view to explore this offline map.
What's next?
Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials:
Not all tutorials are currently available for QML.