Take a screenshot of the map.

Use case
GIS users may want to export a screenshot of a map to enable sharing as an image or printing.
How to use the sample
Pan and zoom to find an interesting location. Press Take screenshot, and a screenshot image will display over the map. Note that there may be a small delay if the map is still rendering when you push the button.
How it works
- The
exportImagefunction is executed on theMapView. - Once the asynchronous task completes, a
QImageis returned. - QML cannot directly render a
QImage. Instead, a QML Image Provider must be created for generating an in-memory URL to the returnedQImage. This is done with the following steps:
- Subclass
QQuickImageProvider. - Reimplement the
requestImagefunction. - Add an image provider to the
QQmlEngineinstance usingQQmlEngine::addImageProvider. - Create a
Q_PROPERTYthat returns the image URL from the Image Provider. - Bind the QML Image to the image URL property. When this property changes, it will automatically trigger the
requestImagefunction and return the URL to the image.
Relevant API
- GeoView::exportImage
Tags
capture, export, image, print, screen capture, screenshot, share, shot
Sample Code
// [Legal]// Copyright 2018 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "MapImageProvider.h"
// Qt headers#include <QQuickImageProvider>
MapImageProvider::MapImageProvider() : QQuickImageProvider(QQuickImageProvider::Image){}
// reimplemented function for QML to request Images from the providerQImage MapImageProvider::requestImage(const QString& id, QSize* size, const QSize &requestedSize){ Q_UNUSED(size) Q_UNUSED(requestedSize) return m_images[id];}
// helper to add images to the the providervoid MapImageProvider::addImage(const QString& id, const QImage& img){ m_images[id] = img;}
// static function to return the image provider idQString MapImageProvider::imageProviderId(){ return QStringLiteral("mapimageprovider");}// [Legal]// Copyright 2018 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
#ifndef MAPIMAGEPROVIDER_H#define MAPIMAGEPROVIDER_H
// Qt headers#include <QImage>#include <QMap>#include <QQuickImageProvider>
class MapImageProvider : public QQuickImageProvider{
public: MapImageProvider(); ~MapImageProvider() override = default;
public: QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; void addImage(const QString& id, const QImage& img); static QString imageProviderId();
private: QMap<QString, QImage> m_images;};
#endif // MAPIMAGEPROVIDER_H// [WriteFile Name=TakeScreenshot, Category=Maps]// [Legal]// Copyright 2018 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "MapImageProvider.h"#include "TakeScreenshot.h"
// ArcGIS Maps SDK headers#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"
// Qt headers#include <QFuture>#include <QQmlContext>#include <QUuid>
using namespace Esri::ArcGISRuntime;
TakeScreenshot::TakeScreenshot(QQuickItem* parent /* = nullptr */): QQuickItem(parent){}
void TakeScreenshot::init(){ // Register the map view for QML qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<TakeScreenshot>("Esri.Samples", 1, 0, "TakeScreenshotSample");}
void TakeScreenshot::componentComplete(){ QQuickItem::componentComplete();
// find QML MapView component m_mapView = findChild<MapQuickView*>("mapView");
// Create a map using the imagery basemap m_map = new Map(BasemapStyle::ArcGISImageryStandard, this);
// Set map to map view m_mapView->setMap(m_map);
// Get the image provider from the QML Engine QQmlEngine* engine = QQmlEngine::contextForObject(this)->engine(); m_imageProvider = dynamic_cast<MapImageProvider*>(engine->imageProvider(MapImageProvider::imageProviderId()));}
// Q_INVOKABLE function to kick off the export image asynchronous taskvoid TakeScreenshot::captureScreenshot(){ m_mapView->exportImageAsync().then(this, [this](QImage img) { // convert the QUuid into a QString const QString imageId = QUuid().createUuid().toString(QUuid::WithoutBraces); // add the image to the provider m_imageProvider->addImage(imageId, img); // update the URL with the unique id m_mapImageUrl = QString("image://%1/%2").arg(MapImageProvider::imageProviderId(), imageId); // emit the signal to trigger the QML Image to update emit mapImageUrlChanged(); });}
QUrl TakeScreenshot::mapImageUrl() const{ return m_mapImageUrl;}// [WriteFile Name=TakeScreenshot, Category=Maps]// [Legal]// Copyright 2018 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef TAKESCREENSHOT_H#define TAKESCREENSHOT_H
// Qt headers#include <QQuickItem>
namespace Esri::ArcGISRuntime{class Map;class MapQuickView;}
class MapImageProvider;
class TakeScreenshot : public QQuickItem{ Q_OBJECT
Q_PROPERTY(QUrl mapImageUrl READ mapImageUrl NOTIFY mapImageUrlChanged)
public: explicit TakeScreenshot(QQuickItem* parent = nullptr); ~TakeScreenshot() override = default;
void componentComplete() override; static void init(); Q_INVOKABLE void captureScreenshot();
signals: void mapImageUrlChanged();
private: Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; MapImageProvider* m_imageProvider = nullptr; QUrl m_mapImageUrl;
private: QUrl mapImageUrl() const;};
#endif // TAKESCREENSHOT_H// [WriteFile Name=TakeScreenshot, Category=Maps]// [Legal]// Copyright 2018 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuickimport QtQuick.Controlsimport Esri.Samples
TakeScreenshotSample { id: rootRectangle clip: true width: 800 height: 600
// Declare a MapView MapView { id: mapView anchors.fill: parent objectName: "mapView"
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); }
Button { anchors { horizontalCenter: parent.horizontalCenter bottom: mapView.attributionTop margins: 10 } text: "Take screenshot" onClicked: { captureScreenshot(); busyIndicator.visible = true; } } }
onMapImageUrlChanged: { imageRect.visible = true; busyIndicator.visible = false; }
Rectangle { id: imageRect anchors.fill: parent visible: false Rectangle { anchors.fill: parent color: "#60000000" }
Image { id: mapImage anchors.centerIn: parent width: parent.width * 0.75 height: parent.height * 0.75 source: mapImageUrl // bind to the property exposed by TakeScreenshot
Rectangle { anchors { right: parent.right top: parent.top margins: 10 } width: 28 height: width color: "lightgray" radius: 50
Image { anchors.centerIn: parent width: parent.width * 0.95 height: parent.height * 0.95 source: "qrc:/Samples/Maps/TakeScreenshot/close.png" }
MouseArea { anchors.fill: parent onClicked: imageRect.visible = false; } } } }
BusyIndicator { id: busyIndicator anchors.centerIn: parent visible: false }}// [Legal]// Copyright 2018 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "MapImageProvider.h"#include "TakeScreenshot.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QCommandLineParser>#include <QDir>#include <QGuiApplication>#include <QQmlContext>#include <QQmlEngine>#include <QQuickView>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
#define STRINGIZE(x) #x#define QUOTE(x) STRINGIZE(x)
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false); QGuiApplication app(argc, argv); app.setApplicationName(QString("TakeScreenshot"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 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("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample TakeScreenshot::init();
// Initialize application view QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView);
QString arcGISRuntimeImportPath = QUOTE(ARCGIS_RUNTIME_IMPORT_PATH);
#if defined(LINUX_PLATFORM_REPLACEMENT) // on some linux platforms the string 'linux' is replaced with 1 // fix the replacement paths which were created QString replaceString = QUOTE(LINUX_PLATFORM_REPLACEMENT); arcGISRuntimeImportPath = arcGISRuntimeImportPath.replace(replaceString, "linux", Qt::CaseSensitive);#endif
// Add the import Path view.engine()->addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml")); // Add the Runtime path view.engine()->addImportPath(arcGISRuntimeImportPath);
// Add Image Provider to the QQmlEngine instance const QString name = MapImageProvider::imageProviderId(); view.engine()->addImageProvider(name, new MapImageProvider);
// Set the source view.setSource(QUrl("qrc:/Samples/Maps/TakeScreenshot/TakeScreenshot.qml"));
view.show();
return app.exec();}