Web map printing with arcpy.mp

A common arcpy.mp workflow used in Portal for ArcGIS is web map printing, in which Python, ArcGIS Experience Builder, ArcGIS API for JavaScript and ArcGIS Web AppBuilder work together to create web apps that produce high-quality cartographic output.

The ConvertWebMapToArcGISProject function

The ConvertWebMapToArcGISProject function converts a web map that you intend to print or export to an ArcGIS AllSource project. Once the web map is converted, the full state of the web map exists in the project. The project can then be further modified before being printed or exported to a common format such as PDF. The ConvertWebMapToArcGISProject function is commonly used when printing a map from a web GIS app using ArcGIS Experience Builder, ArcGIS API for JavaScript or ArcGIS Web AppBuilder.

Advantages of arcpy.mp in web map printing

The ConvertWebMapToArcGISProject function is intended for workflows in which a web map must be modified or exported using arcpy.mp functions. Some example workflows that can be met using the ConvertWebMapToArcGISProject function are as follows:

  • Exporting using advanced options—All of the arcpy.mp export functions have advanced options. For example, the exportToPDF method on the Layout and MapView classes has parameters for controlling raster and vector compression, defining colorspace, embedding fonts, and so on. For a code sample, see example 2 in ConvertWebMapToArcGISProject.
  • Creating map books—A map book can be generated if a Spatial Map Series is enabled on the staged layout template. The output layout can also be exported as a PDF file and inserted into other PDF files (for example, a title page or report) using the PDFDocument class to create a complete map book. For code samples, see examples 4 and 5 in ConvertWebMapToArcGISProject.
  • Creating reports—A report can be generated for layers in the web map or staged layout templates. One way to accomplish this workflow is to import a report file (.rptx) into the ArcGIS AllSource project that is returned from ConvertWebMapToArcGISProject. Use the importDocument function on the ArcGISProject class to import a report file. The report can be appended to other PDF files (for example, a layout) using the PDFDocument class. For a code sample, see example 6 in ConvertWebMapToArcGISProject.
  • Displaying selected features in a table frame or table attribute dynamic text. Python CIM access can be used to access features not available in arcpy.mp, such as table frames. For a code sample, see example 7 in ConvertWebMapToArcGISProject.
Note:

ArcGIS Server also includes a geoprocessing service called PrintingTools. The PrintingTools service can be used in a web app to generate a high-cartographic-quality printable image. For more information regarding the PrintingTools service, see Printing in web applications.

The output file (for example, .pdf, .png, and so on) of the Python script can be a layout from the optional template_pagx parameter and can include page layout surrounds (for example, title text, legends, scale bar, overview map frames, grids, graticules, tables, charts, and so on). The output can also be a MapView, which would not include any page layout surrounds.

Share your script as a web tool

Once you have a Python script that prepares the map for printing, you can encapsulate it in a script tool. You can then publish the script tool as a web tool. ArcGIS Experience Builder, ArcGIS API for JavaScript and ArcGIS Web AppBuilder have a Print task or a Print widget that you can use in your web GIS app. Both the Print task and Print widget have a URL property that points to the REST URL of the web tool you created.

Script tool parameters

When using ConvertWebMapToArcGISProject in a web tool in ArcGIS Experience Builder, ArcGIS API for JavaScript or ArcGIS Web AppBuilder, the parameter names of the script tool must match the Print task or Print widget parameters described in the following table:

Script tool parameter nameData typeTypeDescription
Web_Map_as_JSON

String

Required

A JavaScript Object Notation (JSON) representation of the state of the map to be exported as it appears in the web app. JSON is the format of the web map. It contains the full state of the web map (for example, layers, coordinate system, extent, scale, and so on). ArcGIS Experience Builder, ArcGIS API for JavaScript and ArcGIS Web AppBuilder allow you to get this JSON string from the web app.

Output_File

File

Derived

The output file name. The extension of the file depends on the Format parameter.

Format

String

Optional

The format in which the map image for printing will be delivered. The following strings are accepted: PDF, PNG, PNG8, PNG32, JPG, GIF, EPS, SVG, and SVGZ.

Layout_Template

String

Optional

Either a name of a template from the list or the keyword MAP_ONLY. When MAP_ONLY is chosen or an empty string is passed in, the output map does not contain any page layout surroundings (for example, title, legends, scale bar, and so on).

The following screen capture shows a sample script tool's parameters:

Sample script tool parameters

Tip:

When working with ArcGIS API for JavaScript, any number of additional user-defined parameters can also be added. The ability to pass extra parameters into a custom Print task is useful, as it allows you to collect any number of extra parameters from the web app and pass them into the Python script. For example, your web app may have a control that allows users to choose whether to embed georeferencing information in the output PDF. For more information on collecting extra parameters, see example 3 in the Examples of using custom Python scripts in web apps section below.

For more information regarding setting script tool parameters, see Setting script tool parameters.

Understand web map JSON

When you use the ArcGIS Experience Builder, ArcGIS API for JavaScript or ArcGIS Web AppBuilder Print widget or Print task, respectively, you don't need to create the web map JSON; the APIs take care of it for you. However, before the script can be published and used in the web APIs, it must be run locally in ArcGIS AllSource. Any valid web map JSON string can be used when running the script locally. A JSON string similar to what your web app will be returning may be required for your script to run successfully. See ExportWebMap specification to understand how this text should be formatted. The following is a sample string:

{
    "layoutOptions": {
        "titleText": "Simple WebMap JSON example"
    },
    "operationalLayers": [
        {
            "url": "https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Federal_Lands/FeatureServer/0",
            "visibility": true,
            "title": "USA_Federal_Lands"
        }
    ],
    "exportOptions": {
        "outputSize": [
            1500,
            1500
        ]
    },
    "mapOptions": {
        "extent": {
            "xmin": -13077000,
            "ymin": 4031000,
            "xmax": -13023000,
            "ymax": 4053000
        }
    },
    "version": "1.4"
}
Tip:

When running the script tool, the JSON string can be copied and pasted into the Web_Map_as_JSON input parameter. However, the line breaks must be removed for the string to be valid input. The following is a sample JSON string with line breaks removed:

{"layoutOptions": {"titleText": "Simple WebMap JSON example"},"operationalLayers": [{"url": "https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Federal_Lands/FeatureServer/0","visibility": true, "title": "USA_Federal_Lands"}],"exportOptions": {"outputSize": [1500,1500]},"mapOptions": {"extent": {"xmin": -13077000,"ymin": 4031000,"xmax": -13023000,"ymax": 4053000}},"version": "1.4"}
Tip:

Alternatively, the Python script can be authored in such a way to allow for empty Web_Map_as_JSON input when running from ArcGIS AllSource prior to publishing. There are several ways in which this can be accomplished. The code example below demonstrates one way of doing this. Once the script has been shared as a web tool, it can be further tested with valid JSON from the web app.

# If script is executed from within ArcGIS Pro, and WebMap_as_JSON is blank, then don't fail
prodName = arcpy.GetInstallInfo()['ProductName']
if (WebMap_as_JSON == '#'):
	if (prodName == 'ArcGISPro'):
		exit()
Tip:

As mentioned previously, the web map JSON returned from the web app contains the full state of the web map. The layoutOptions object in the web map JSON warrants extra discussion, as it will automatically update layout elements that can be staged in the template_pagx. For example, if the JSON has a titleText setting, and the template_pagx has a Layout Title or Metadata dynamic text element with a title attribute, the dynamic text in the template page layout will be updated with the titleText value. For more information, see the layoutOptions section in the ExportWebMap specification.

Layout templates

ArcGIS AllSource allows you to create rich cartographic layouts that can be saved as a layout file (.pagx) to use as the optional template_pagx parameter in ConvertWebMapToArcGISProject. The page layout can contain map surrounds (for example, title text, legends, scale bar, overview map frames, grids, graticules, tables, charts, and so on). The map surrounds can either be static or dynamic. This means they can optionally react to changes in the map. For example, grids and graticules can automatically change based on scale, or legend items can only show the features that are in the current extent. For more information, see Layouts in ArcGIS Pro and Make a layout tutorial.

When the Python script that uses ConvertWebMapToArcGISProject is encapsulated in a web tool, you must ensure that ArcGIS Server can access the layout templates and data used in the web app. It is best practice to use a folder that is registered with ArcGIS Server. For more information on registering data, see Registering data with ArcGIS Server.

In addition to creating your own layout templates, the templates that are included with the software can also be used. They are located at <installation_directory>\Templates\ExportWebMapTemplates. These templates contain map surround elements such as a legend, current date dynamic text, a scale bar, and scale text. These templates are a good starting point. However, keep in mind that they are part of the installation directory and will be removed if the software is uninstalled or reinstalled. These templates can be manually copied to a location that ArcGIS Server can access and can be further modified if required.

In certain scenarios, it may be beneficial have a separate task in the web app to get information about the available layout templates. For example, you may want to know if the end user of the web app chose a layout template with a Layout Metadata dynamic text element with a title attribute so you can prompt the user to enter their own custom title. To accomplish this, the PrintingTools service has a task called Get Layout Templates Info that returns the content of layout templates in JSON format. For more information regarding the Get Layout Templates Info task, see Printing in web applications.

Client-side graphics support

By default, notes overlays or client-side graphics from the web app are stored in an in-memory workspace. The in-memory workspace is temporary and is deleted when the app is closed. To make a permanent copy of the output project that contains notes overlays, specify a notes_gdb and use the saveACopy method from the ArcGISProject class.

Using the updateLayerFromJSON function

If your web app uses dynamic layers, the updateLayerFromJSON function from the Layer class can be used to update the properties (for example, symbology) of staged vector layers in the layout template with the layer definition of dynamic layers from the web map JSON. This is useful if your web app allows changing the symbology of dynamic layers, and you want to replace the service layers with staged vector data but still maintain the updated symbology from the web app. For an example of using updateLayerFromJSON, see example 3 in ConvertWebMapToArcGISProject.

Print maps that contain non-token-based secured services from ArcGIS Server

The ImportCredentials function can be used to access ArcGIS Server non-token-based secured services from GIS server connection files. Credentials can be stored in connection files that are created in ArcGIS AllSource. See Connect to a GIS server for more information regarding creating connection files. In web map-printing scripts, use ImportCredentials before ConvertWebMapToArcGISProject. The following connection files types are supported:

  • ArcGIS Server connection file (.ags)
  • WMS Server connection file (.wms)
  • WMTS Server connection file (.wmts)

Credentials can be cleared when finished using arcpy.ClearCredentials.

For a code sample, see example 8 in ConvertWebMapToArcGISProject.

For more information, see Print maps that contain secured services.

Caution:

It is not recommended that you embed credentials in a custom print service without fully understanding the security impact. If you choose to publish your own service for printing with embedded credentials, it's recommended that you apply ArcGIS Server security rules to restrict who can access the service. This prevents anonymous users from generating printable map images that display your secured services. See Modify permissions for a service or folder to learn more about setting up security.

Examples of using custom Python scripts in web apps

The following sections show examples of using custom Python scripts in web apps.

Example 1: Use a custom Python script in ArcGIS Experience Builder

After sharing the Python script tool as a web tool, the Print widget in ArcGIS Experience Builder can be configured to use the web tool.

The Print widget has a Print template setting which allows you to add a print service and configure templates. In Add utility, you can enter the REST URL of the web tool. For example:

https://MyServer:6443/arcgis/rest/services/MyPrintService/GPServer

Once you select a print service, the service automatically adds print templates.

Example 2: Use a custom Python script in ArcGIS Web AppBuilder

After sharing the Python script tool as a web tool, the Print widget in ArcGIS Web AppBuilder is configured to use the REST URL of the web tool. The following screen capture shows the configuration dialog box for the Print widget:

The configuration dialog box for the Print widget
Tip:

In the screen capture above, the values for Default format and Default layout are automatically populated from the Format and Layout_Template parameters from the web tool.

Example 3: Use a custom Python script in ArcGIS API for JavaScript

This example shows how Python and ArcGIS API for JavaScript can be used together to create a web GIS app for web map printing. The user will be able to do the following in the web app:

  • Navigate to an area of interest.
  • Select a layout template.
  • Select an output format.
  • Choose whether to output a tagged PDF file where text can be read by screen readers or other assistive technology by passing in an extra parameter from the web app to the Print task.

This Python script shows how to pass extra parameters (pdf_accessibility) from the web app to the Python script beyond the standard parameters supported by the Print task.

import arcpy
import os
import uuid

# The template location in the server data store
templatePath = '//MyServer/MyDataStore/Templates'

# Input WebMap JSON
Web_Map_as_JSON = arcpy.GetParameterAsText(0)

# Format for output
Format = arcpy.GetParameterAsText(1)

# Input layout template
Layout_Template = arcpy.GetParameterAsText(2)
    
# Extra parameter - pdf_accessibility
Access_info = arcpy.GetParameterAsText(3)

# Convert Access_info string to boolean
if Access_info.lower() == 'false': 
    Access_info_bol = False
elif Access_info.lower() == 'true': 
    Access_info_bol = True
else: Access_info_bol = True

# Get the requested layout template pagx file
templatePagx = os.path.join(templatePath, Layout_Template + '.pagx')
   
# Convert the WebMap to an ArcGISProject
result = arcpy.mp.ConvertWebMapToArcGISProject(Web_Map_as_JSON, templatePagx, "Layers Map Frame")
aprx = result.ArcGISProject
layout = aprx.listLayouts()[0]
        
# Use the uuid module to generate a GUID as part of the output name
# This will ensure a unique output name
output = 'WebMap_{}.{}'.format(str(uuid.uuid1()), Format)
Output_File = os.path.join(arcpy.env.scratchFolder, output)

# Export the WebMap - use Access_info_bol to control accessibility
if Format.lower() == 'pdf':
    layout.exportToPDF(Output_File, pdf_accessibility=Access_info_bol) 
elif Format.lower() == 'png':
    layout.exportToPNG(Output_File)

# Set the output parameter to be the output file of the server job
arcpy.SetParameterAsText(4, Output_File)

The Python script tool is shared as a web tool. In the following script, the Print task in ArcGIS API for JavaScript is configured to use the REST URL of the web tool:

Tip:

The REST URL of the web tool is referenced in the script at this line:

var printServiceUrl = "https://MyServer:6443/arcgis/rest/services/MyPrintService/GPServer/MyPrintService";

The entire script is as follows:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Austin Print WebApp</title>

  <link rel="stylesheet" href="https://js.arcgis.com/4.3/esri/css/main.css">
  <script src="https://js.arcgis.com/4.3/"></script>

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
    
    #layerToggle {
      top: 20px;
      right: 20px;
      position: absolute;
      z-index: 99;
      background-color: white;
      border-radius: 8px;
      padding: 10px;
      opacity: 1;
    }
  </style>

  <script>
    require([
        "esri/Map",
        "esri/views/MapView",
        "esri/layers/TileLayer",
        "esri/tasks/PrintTask",
        "esri/tasks/support/PrintParameters",
        "esri/tasks/support/PrintTemplate",
        "dojo/dom",
        "dojo/on",
        "dojo/domReady!"
      ],
      function(
        Map, MapView, TileLayer, PrintTask, PrintParameters, PrintTemplate, dom, on
      ) {

        /*****************************************************************
         * Create a TileLayer instance. 
         *****************************************************************/
        var austinLyr = new TileLayer({
          url: "http://MyServer:6080/arcgis/rest/services/MyMapService/MapServer",
          id: "austin",
          opacity: 0.9
        });

        /*****************************************************************
         * Layers may be added to the map in the map's constructor
         *****************************************************************/
        var map = new Map({
          basemap: "oceans",
          layers: [austinLyr]
        });

        var view = new MapView({
          container: "viewDiv",
          map: map
        });
        
        /*****************************************************************
         * Go to extent of Austin Layer
         *****************************************************************/
        view.then(function() {
          austinLyr.then(function() {
            view.goTo(austinLyr.fullExtent);
          });
        });

        var printServiceUrl = "https://MyServer:6443/arcgis/rest/services/MyPrintService/GPServer/MyPrintService";
        
        var printMode = "async";
        
        var printTask = new PrintTask({
   			url: printServiceUrl,
   			mode: printMode
			});
		
	function myPrint() {
		// input layout template
		var layout = dom.byId("layout");
	        var index = layout.selectedIndex;
	        var selectedValue_layout = layout.options[index].value;
	        
	        // format for output
	        var format = dom.byId("format");
	        var index = format.selectedIndex;
	        var selectedValue_format = format.options[index].value;
	        
	        // Extra parameter: Accessibility info boolean
	        var access_info = dom.byId("access_info");
	        var index = access_info.selectedIndex;
	        var selectedValue_access_info = access_info.options[index].value;
	        
	        var template = new PrintTemplate({
			 format: selectedValue_format,
			 layout: selectedValue_layout, 
			});
	
		var params = new PrintParameters({
			 view: view,
			 template: template
			});
			
		params.extraParameters = {
          		Access_info : selectedValue_access_info
        		};
	        
		printTask.execute(params).then(printResult, printError);
			}
		
		function printResult(result) {
        	window.open(result.url);
     		}	
     		
     	function printError(result) {
        	alert('Error printing.')
     		}
     		
     	// Call myPrint() each time the button is clicked    
      	on(dom.byId("doBtn"), "click", myPrint);

      });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
  <span id="layerToggle">
    Layout Template:
    <select id="layout" >
      <OPTION value="Austin26x28">Austin26x28</OPTION>
      <OPTION value="Austin24x36">Austin24x36</OPTION>
    </select>
    &nbsp;&nbsp;Format:
    <select id="format">
      <OPTION value="PDF">PDF</OPTION>
      <OPTION value="PNG">PNG</OPTION>
    </select>
	
    &nbsp;&nbsp;Include Accessibility info?
    <select id="access_info">
      <OPTION value="True">True</OPTION>
      <OPTION value="False">False</OPTION>
    </select>
    <button id="doBtn">Print Map</button>
  </span>
</body>

</html>