Web map printing with arcpy.mp

A common arcpy.mp workflow used in Portal for ArcGIS is web map printing, in which Python, 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 Pro 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 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:

  • Swapping out service layers for local vector data—In arcpy.mp scripts, service layers can be identified and swapped out for layers that point to local data. This is commonly used when a vector output is preferred rather than service layers. Some advantages to vector output are as follows:
    • Vector PDF output supports embedding feature attributes in PDF viewing applications. Switching layer visibility and viewing map coordinates are also supported but do not require vector.
    • Dynamic tables and charts can be associated with vector layers in staged layout templates. They react to changes in the map extent so that only data visible in the map is shown.
    • For high-quality cartographic output, vector data may be preferable to cached map services.
    One way to accomplish swapping service layers for vector data is to stage layout templates that contain vector equivalents of all the possible service layers. After running the ConvertWebMapToArcGISProject function, loop through all the layers in the output map, and remove all layers except the vector layers that correspond to the service layers in the web map. This workflow can be used when swapping your own services with the corresponding vector data that you already have. For a code sample, see example 3 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 5 and 6 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 Pro 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 7 in ConvertWebMapToArcGISProject.
  • 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. The exportToPDF method also has options to embed PDF layers, feature attributes, and georeferencing information in the output PDF file. These features can then be accessed in PDF viewers such as Adobe Reader. For a code sample, see example 3 in ConvertWebMapToArcGISProject.
  • Displaying selected features in a table frame. Python CIM access can be used to access features not available in arcpy.mp, such as table frames. For a code sample, see example 8 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 API for JavaScript and ArcGIS Web AppBuilder have a Print task and a Print widget, respectively, 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 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 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 an example of collecting extra parameters from the web app, see example 3 in ConvertWebMapToArcGISProject.

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

Understand web map JSON

When you use the ArcGIS API for JavaScript or ArcGIS Web AppBuilder Print task or Print widget, 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 Pro. 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": "http://maps1.arcgisonline.com/ArcGIS/rest/services/USA_Federal_Lands/MapServer",
            "visibility": true
        }
    ],
    "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": "http://maps1.arcgisonline.com/ArcGIS/rest/services/USA_Federal_Lands/MapServer","visibility": true}],"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 Pro 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 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 Pro 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 4 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 Pro. 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 9 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 Web AppBuilder

This example shows how Python and ArcGIS Web AppBuilder can be used together to create a web GIS app for web map printing. The user will be able to navigate to an area of interest, choose from a list of layout templates (for example, different page sizes), and click a Print button. The output will be a printer-friendly PDF or .png document. If the user chooses PDF output, the file will contain vector data for service layers.

In this Python script, staged layout templates (.pagx files) that contain vector equivalents of all the possible service layers are referenced. After running the ConvertWebMapToArcGISProject function, the script loops through all the layers in the output map, removing all the service layers from the web map JSON, leaving only the staged vector layers. The layout is then exported to either a PDF or .png file.

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)

# 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]

# Reference the map that contains the webmap
m = aprx.listMaps('Web Map')[0]

# Remove the service layer
# This will just leave the vector layers from the template
for lyr in m.listLayers():
    if lyr.isWebLayer:
        m.removeLayer(lyr)
        
# 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
if Format.lower() == 'pdf':
    layout.exportToPDF(Output_File) 
elif Format.lower() == 'png':
    layout.exportToPNG(Output_File)

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

# Clean up
del layout, aprx, result

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 2: 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 staged layout template.
  • Select an output format.
  • Choose whether to export georeferencing information to an output PDF file by passing in an extra parameter from the web app to the Print task.
  • Export the map to a printer-friendly format containing vector output for service layers.

In this Python script, staged layout templates (.pagx files) that contain vector equivalents of all the possible service layers are referenced. After running the ConvertWebMapToArcGISProject function, the script loops through all the layers in the output map, removing all the service layers from the web map JSON, leaving only the staged vector layers. The output layout is then exported to either a PDF or .png file. This example also shows how to pass extra parameters (Georef_info) 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 - georef_info
Georef_info = arcpy.GetParameterAsText(3)

# Convert Georef_info string to boolean
if Georef_info.lower() == 'false': 
    Georef_info_bol = False
elif Georef_info.lower() == 'true': 
    Georef_info_bol = True
else: Georef_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]

# Reference the map that contains the webmap
m = aprx.listMaps('Web Map')[0]

# Remove the service layer
# This will just leave the vector layers from the template
for lyr in m.listLayers():
    if lyr.isWebLayer:
        m.removeLayer(lyr)
        
# 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 Georef_info_bol to control georeferencing
if Format.lower() == 'pdf':
    layout.exportToPDF(Output_File, georef_info=Georef_info_bol) 
elif Format.lower() == 'png':
    layout.exportToPNG(Output_File, world_file=Georef_info_bol)

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

# Clean up
del layout, aprx, result

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: Georeferencing info boolean
	        var georef_info = dom.byId("georef_info");
	        var index = georef_info.selectedIndex;
	        var selectedValue_georef_info = georef_info.options[index].value;
	        
	        var template = new PrintTemplate({
			 format: selectedValue_format,
			 layout: selectedValue_layout, 
			});
	
		var params = new PrintParameters({
			 view: view,
			 template: template
			});
			
		params.extraParameters = {
          		Georef_info : selectedValue_georef_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 Georef info?
    <select id="georef_info">
      <OPTION value="True">True</OPTION>
      <OPTION value="False">False</OPTION>
    </select>
    <button id="doBtn">Print Map</button>
  </span>
</body>

</html>